13.TypeScript徹底解讀-高級類型(2)css
高級類型中文網的地址:https://typescript.bootcss.com/advanced-types.htmlhtml
建立文件並在index.ts內引用node
在ts中this也是一種類型typescript
看一個計算器的例子數組
經過public在constructor上添加屬性count類型爲number類型,默認值爲0promise
定義方法add,裏面把count和傳進來的值相加,返回這個實例分佈式
實例上調用add方法傳入3,輸出結果13函數
接着調用減法的方法,返回11this
咱們在每一個方法後面return this就是這個實例,就能夠實現鏈式調用es5
建立一個子類PowCounter繼承自Counters,並在子類中定義方法pow,這樣在調用子類的時候就繼承了父類的方法add方法和subtract方法還有屬性count
在調用子類的時候,這裏經過子類的點.列出了有三個方法能夠調用
2的三次方返回8
覺得類繼承了Counters類,因此能夠調用父類的方法add
在調用減法
1.7版本增長this類型後,ts會對返回的this進行判斷,會判斷this是一個繼承來的類裏面建立的實例,在調用繼承來的方法就不會報錯了。這就是this類型的使用
包含兩個內容,1索引類型查詢操做符 2索引訪問
//索引類型查詢操做符就是keyof這個操做符,它鏈接一個類型而後返回由這個類型的全部索引屬性名組成的聯合類型
上面定義的是接口,而後定義變量infoProp,類型是上面定義的接口
把鼠標放到infoProp上就看到它變成了兩個字符串組成的聯合類型,要麼是name要麼是age
至關於這個接口的兩個屬性名 字符串組成的聯合類型
給這個變量賦值name和age的字符串都是能夠的。
可是賦值sex就會報錯
經過和泛型的結合使用,ts就能夠檢查使用了動態屬性名的代碼
keyof T就是獲取T裏面全部的屬性名組成的聯合類型,
K extends Keyof T:K的類型只能是T裏面的全部屬性名組成的聯合類型
這裏是泛型約束,打錯字成了檢討了
定義一個函數 getValue 參數Obj類型是T,names參數類型是K的數組。返回值類型這裏暫時還沒寫,寫完函數體再去定義
用map遍歷names,n就是屬性名,函數體內返回obje[n]就是屬性名的屬性值
調用這個方法,
再傳入age的值
返回值類型:T[K],表示T的屬性,表示T的屬性值組成的數組
保存後,編譯器會推薦你 寫成這種形式,自動給你改過來
出錯的緣由是咱們的age屬性是數值類型
若是age屬性也想要的話infoValues的類型就要使用
若是單獨這麼寫:string | number[] 會認爲是string或者number類型組成的數組,因此要用括號括包起來:(string | number)[]這樣寫
保存後編譯器會自動給你推薦寫成這種形式。這樣就不會報錯了。
就是[]
和咱們訪問對象的某個屬性值是同樣的,只不過在ts中,它是用來訪問某個屬性的類型
好比咱們剛纔定義的這個接口
定義一個類型別名NameTypes是接口裏面的name字段,。NameTypes的類型就至關因而string類型
這就是經過接口的索引訪問操做,訪問到了name字段它所對應的類型是string,因此這裏的類型別名也是string類型
返回類型是:T[K]。T上面的K屬性
函數的返回類型
返回的結果是o上面的name
結合接口的例子,key索引的類型爲number,值類型爲T
定義變量kyes類型爲 接口裏面全部屬性名的類型,由於這裏用了keyof
給接口傳一個number的類型,鼠標放過來就能夠看到keys的類型就是number
講索引類型的時候講過,若是索引的類型爲number,那實際實現該接口的對象的屬性名必須是number類型,可是若是這個接口的索引類型是stirng類型的話,那麼實現該接口的對象的屬性名設置爲數值類型是能夠的,
由於數值最後也是會轉換爲字符串
下面修改接口的索引類型設置爲string
那麼這個對象的類型就是string | number聯合類型,就是上面說的那個緣由
也可使用訪問操做符獲取索引的類型
keys類型定義的泛型傳入的是number類型,獲取裏接口內索引的類型 也就是傳入的T ,就是number類型。這裏比較繞。沒明白定義objs1的意義。沒有用到objs1
先定義接口,裏面有不少屬性不少類型,下面定義類型別名 Test 。
ketof Type返回的是接口內不爲never不爲null 不爲undefined的屬性的屬性名
Type實際上就是string、number、object組成的聯合類型
ts提供了藉助舊類型建立新類型的方式就是映射類型。
在映射類型裏,新類型以相同的形式去轉換舊類型裏每一個屬性
ReadonlyType是一個映射類型
keyof T就是傳進來的T的屬性名組成的數組,p in 就表示在這個屬性數組裏進行循環,遍歷每個屬性名
前面加上readonly,他就會在前面加上readonly並返回每個屬性名
這裏調用映射類型並傳入接口 Info1,看起來很像是一個函數,
ReadonlyInfo1就至關於與Info1的接口,只不過他的每一個屬性前面都有readyonly修飾符
定義一個變量info11類型是ReadonlyInfo1的接口類型的,可是由於這個接口的屬性都是隻讀的,因此若是你想改變屬性的值是不能夠的
ts內置了Readonly和Partial這兩種類型,能夠直接就拿來用
Partial每個都是可選的
還有另外兩種映射類型 Pick和Record
鼠標放在pick上,能夠看到是哪一個類下面定義的
在node_modules/typescript/lib.es5.d.ts
傳進來一個對象,再傳一個屬性列表。最後返回的是部分的屬性值
Record的源碼,構造屬性爲K,類型爲K組成的類型
官方文檔沒有補充例子,這裏咱們來補充例子
返回類型是Pick<T , K>,這就是用的映射類型,返回的這個對象T上的一部分屬性名K組成的一個類型
tslint把這個屬性關掉
咱們開始調用這個方法pick,第一個個參數是咱們上面定義的,第二個參數就是info5裏面的能夠被繼承的屬性的數組。
咱們上面就是那個屬性
返回的結果就是name和address組成的聯合類型
打印出結果,就是隻包含name和address的對象。這就是pick的用法
function mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U> { const res: any = {} for (const key in obj) { res[key] = f(obj[key]) } return res } const names = { 0: 'hello', 1: 'world', 2: 'bye' } const lengths = mapObject(names, (s) => s.length) // console.log(lengths)
tslint的錯誤,先關閉
返回了每一個屬性值對應的長度
把對象中的每一個屬性,轉換爲其餘的時候,能夠贏record
每個屬性變成了對象,都有get方法和set方法
調用name屬性的get方法
調用set方法,給name賦值,再再次打印出來修改後的值
任何對他進行拆包,定義一個函數,在這個函數裏進行拆包
打印出了處理以後的對象,有映射類型進行推斷,推斷原始類型
用+或-
把readonly屬性就去掉了。
這樣屬性就都是可選的了
減去問號
可選屬性就都去掉了
keyof Objs2就是訪問Objs2全部的屬性名,
在看一個映射類型的例子,
修改a的屬性就會報錯了,由於a是一個制度的屬性。這就是屬性名對number類型和symbol類型的支持
ts3.1會生成新的元組和數組類型,並不會常見一個新的類型
promiseType的參數類型必須是三個Promise分別是Number、String、boolean的類型
頂級類型
// [1] 任何類型均可以賦值給unknown類型
// [1] 任何類型均可以賦值給unknown類型 let value1: unknown value1 = 'a' value1 = 123
// [2] 若是沒有類型斷言或基於控制流的類型細化時,unknown不能夠賦值給其餘類型,此時他只能賦值給unknown和any類型
unkenow賦值給unknow是能夠的
// [3] 若是沒有類型斷言或基於控制流的類型細化時,不能在他上面進行任何操做
對象的類型爲unknow類型不能作加法操做
// [4] unknown與任何其餘類型組成的交叉類型,最後都等於其餘類型
unknow和string的數組最終返回string的數組
// [5] unknown與任何其餘類型(除了any是any)組成的聯合類型,都等於unknown類型
// [6] never類型是unknown的子類型
這裏使用條件類型,這裏用到了三元操做符,never extends unknown若是爲真 就取的是true 不然取的是false
因此最終返回了true
// [7] keyof unknown 等於類型never
// [8] 只能對unknown進行等或不等操做,不能進行其餘操做
// [9] unknown類型的值不能訪問他的屬性、做爲函數調用和做爲類建立實例
調用屬性值,和把它當方法調用都是不能夠的
// [10] 使用映射類型時若是遍歷的是unknown類型,則不會映射任何屬性
傳入unknown類型,返回空的數組
ts在2.8引入的,從語法上看像是一個三元操做符
判斷傳入的 T是不是string類型,若是是就返回string若是不是就返回number類型
傳入了字符串a 因此返回的是string類型
傳入123返回的就是數字類型了
分佈式條件類型
傳入一個聯合類型
string和number都是any的子類型,因此傳入了聯合類型就返回了聯合類型
官方的示例
傳入一個函數類型,返回的就是函數類型了
傳入stirng[].就是string組成的數組,都沒有符合的就返回了object類型
最終返回object和函數的藍河類型。由於string[]沒有這個選項,因此返回的是object
再來看一個實際的分佈式條件類型的應用
type Diff<T, U> = T extends U ? never : T type Test2 = Diff<string | number | boolean, undefined | number>
條件類型和映射類型結合的例子
遍歷傳入的屬性值,若是屬性值是函數類型就返回K不然就返回never
修改一下,函數用括號括起來
在後面使用索引訪問類型,裏面的索引是keyof T
在定義一個接口
tslint提示這裏不用用Function,這裏須要關閉一下
接口Part有四個字段,updatePart是一個函數類型
K in keyof T:用來遍歷T的全部屬性名,它的值使用了條件類型:
T[K]:表示當前屬性名的屬性值,判斷它是都爲函數類型。若是是反返回當前名的字符串,若是不是就返回never類型。結尾來來使用[ketof T]獲取T的屬性名,最後經過索引訪問類型就是[ketof T]來獲取做不never的類型
因此最終調用會返回類型不爲never的類型,因此最終返回undatePart函數名
改爲兩個函數
返回兩個函數名組成的兩個聯合類型
不是用infer的狀況
判斷T是不是一個數組any[],若是是T[number]:索引訪問類型,經過傳入一個number索引獲取到的實際上是它的值的類型。
不然直接返回類型T
若是是數組返回數組元素的類型,不是數組,直接取這個類型
條件爲false直接返回T
使用infer的寫法
使用infer來修飾U:infer能夠推斷U裏面元素的類型,而且記錄在U裏面,返回的U就是推斷出來的類型
傳入string[]返回string類型
傳進來的不是數組,
在ts2.8的版本增長的
Exclue:從前面這個類型中選在出不在後面這個類型中的類型
// Exclude<T, U> type Type10 = Exclude<'a' | 'b' | 'c', 'a'>
選取T中能夠複製給U的類型
// Extract<T, U> type Type11 = Extract<'a' | 'b' | 'c', 'c' | 'b'>
NonNullable,在T中去掉null個undefined
// NonNullable<T> type Type12 = NonNullable<string | number | null | undefined>
獲取函數類型返回值類型
// ReturnType<T> type Type13 = ReturnType<() => string> type Type14 = ReturnType<() => void>
沒有返回類型的函數
InstanceType<T>:獲取構造函數的實例類型
很差理解,先看下庫文件裏面的實現
T必須是一個構造函數類型
返回值無所謂,any類型
條件判斷,判斷T是不是構造函數類型
用infer推斷出R的類型
傳入類型A
返回的類型也是A
傳入上面定義的AClass,那麼類型就是AClass。這裏爲何要用typeof AClass呢,由於Aclass是一個值,不是一個類型,要獲取它的類型就必須用typeof
傳入any類型就是any
傳入never返回的就是never類型
傳入stirng類型報錯,string不知足構造函數類型
再來理解下源碼:
T是不是構造函數,推斷出T的返回值
是構造函數,或者構造函數的子類型,就返回構造函數推斷出來的返回值類型R,也就是構造函數返回類型。
若是不是就返回一個any