第二課, 基礎類型和入門高級類型node
第三課, 泛型git
第四課, 解讀高級類型github
第五課, 什麼是命名空間(namespace)?typescript
第二課的時候爲了更好的講解基礎類型, 因此咱們講解了一部分高級類型, 好比"接口( interface )" / "聯合類型( | )" / "交叉類型( & )", 本節課我會把剩餘高級類型都講完.segmentfault
本節課主要關鍵詞爲: 自動類型推斷 / 類型斷言 / 類型別名(type) / 映射類型(Pick/Record等...) / 條件類型(extends) / 類型推斷(infer)api
第二課咱們講了那麼多基礎類型, 你們如今寫ts的時候必定會在每一個變量後面都加上類型吧? 可是?數組
如今告訴你們有些狀況下你不須要標註類型, ts能夠根據你寫的代碼來自動推斷出類型:瀏覽器
let n = 1; // ts會自動推斷出n是number類型 n+=3 // 不報錯,由於已知類型 let arr1 = []; // 類型爲: any[] arr1.push(1,'2', {o:3}); let arr = [1]; // 內部要有數字, 才能推斷出正確類型 arr.push(2);
let n: number|null = 0.5 < Math.random() ? 1:null; if(null !== n){ n+=3 // ts知道如今n不是null是number }
document.ontouchstart = ev=>{ // 能自動推斷出ev爲TouchEvent console.log(ev.touches); // 不報錯, TouchEvent上有touches屬性 }
typeof
就是js中的typeof
, ts會根據你代碼中出現的typeof
來自動推斷類型:dom
let n:number|string = 0.5 < Math.random()? 1:'1'; // 若是沒有typeof, n*=2會報錯, 提示無法推斷出當前是number類型, 不能進行乘法運算 if('number' === typeof n) { n*= 2; } else { n= '2'; }
注意: 在ts文檔中, 該部分的知識點叫作typeof類型保護, 和其餘類型推斷的內容是分開的, 被寫在高級類型/類型保護章節中.
ts會根據你代碼中出現的instanceof
來自動推斷類型:
let obj = 0.5 < Math.random() ? new String(1) : new Array(1); if(obj instanceof String){ // obj推斷爲String類型 obj+= '123' } else { // obj爲any[]類型 obj.push(123); }
注意: 在ts文檔中, 該部分的知識點叫作instanceof類型保護, 和其餘類型推斷的內容是分開的, 被寫在高級類型/類型保護章節中.
有些狀況下系統沒辦法自動推斷出正確的類型, 就須要咱們標記下, 斷言有2種語法, 一種是經過"<>", 一種經過"as", 舉例說明:
let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[] // 斷言, 告訴ts, obj爲數組 (<number[]>obj).push(1); //等價 (obj as number[]).push(1);
類型別名能夠表示不少接口表示不了的類型, 好比字面量類型(經常使用來校驗取值範圍):
type A = 'top'|'right'|'bottom'|'left'; // 表示值多是其中的任意一個 type B = 1|2|3; type C = '紅'|'綠'|'黃'; type D = 150; let a:A = 'none'; // 錯誤, A類型中沒有'none'
interface A1{ a:number; } type B = A1 | {b:string}; type C = A1 & {b:string}; // 與泛型組合 type D<T> = A1 | T[];
js中的Object.keys
你們確定都用過, 獲取對象的鍵值, ts中的keyof
和他相似, 能夠用來獲取對象類型的鍵值:
type A = keyof {a:1,b:'123'} // 'a'|'b' type B = keyof [1,2] // '1'|'2'|'push'... , 獲取到內容的同時, 還獲得了Array原型上的方法和屬性(實戰中暫時沒遇到這種需求, 瞭解便可)
能夠得到鍵值, 也能夠獲取對象類型的值的類型:
type C = A['a'] // 等於type C = 1; let c:C = 2 // 錯誤, 值只能是1
映射類型比較像修改類型的工具函數, 好比Readonly
能夠把每一個屬性都變成只讀:
type A = {a:number, b:string} type A1 = Readonly<A> // {readonly a: number;readonly b: string;}
打開node_modules/typescript/lib文件夾能夠找到lib.es5.d.ts
, 在這咱們能找到Readonly
的定義:
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
其實不是很複雜, 看了本節課前面前面的內容, 這個很好理解是吧:
T
.keyof
獲取T
上的鍵值集合.in
表示循環keyof
獲取的鍵值.readonly
標記.type A = {a:number, b:string} type A1 = Partial<A> // { a?: number; b?: string;}
type A = {a?:number, b?:string} type A1 = Required<A> // { a: number; b: string;}
type A = {a:number, b:string} type A1 = Pick<A, 'a'> // {a:number}
type A = {a:number, b:string} type A1 = Omit<A, 'a'> // {b:string}
type A1 = Record<string, string> // 等價{[k:string]:string}
type A = {a:number, b:string} type A1 = Exclude<number|string, string|number[]> // number // 兼容 type A2 = Exclude<number|string, any|number[]> // never , 由於any兼容number, 因此number被過濾掉
type A = {a:number, b:string} type A1 = Extract<number|string, string|number[]> // string
type A1 = NonNullable<number|string|null|undefined> // number|string
type A1= ReturnType<()=>number> // number
ts中類有2種類型, 靜態部分的類型和實例的類型, 因此T
若是是構造函數類型, 那麼InstanceType
能夠返回他的實例類型:
interface A{ a:HTMLElement; } interface AConstructor{ new():A; } function create (AClass:AConstructor):InstanceType<AConstructor>{ return new AClass(); }
返回類型爲元祖, 元素順序同參數順序.
interface A{ (a:number, b:string):string[]; } type A1 = Parameters<A> // [number, string]
和Parameters
相似, 只是T
這裏是構造函數類型.
interface AConstructor{ new(a:number):string[]; } type A1 = ConstructorParameters<AConstructor> // [number]
T extends U ? X : Y
用來表示類型是不肯定的, 若是U
的類型能夠表示T
, 那麼返回X
, 不然Y
. 舉幾個例子:
type A = string extends '123' ? string :'123' // '123' type B = '123' extends string ? string :123 // string
明顯string
的範圍更大, '123'
能夠被string
表示, 反之不可.
單詞自己的意思是"推斷", 實際表示在extends
條件語句中聲明待推斷的類型變量. 咱們上面介紹的映射類型中就有不少都是ts在lib.d.ts
中實現的, 好比Parameters
:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
第二課的時候爲了更好的講解基礎類型, 因此咱們講解了一部分高級類型, 好比"接口( interface )" / "聯合類型( | )" / "交叉類型( & )", 本節課我會把剩餘高級類型都講完.
本節課主要關鍵詞爲: 自動類型推斷 / 類型斷言 / 類型別名(type) / 映射類型(Pick/Record等...) / 條件類型(extends) / 類型推斷(infer)
第二課咱們講了那麼多基礎類型, 你們如今寫ts的時候必定會在每一個變量後面都加上類型吧? 可是?
如今告訴你們有些狀況下你不須要標註類型, ts能夠根據你寫的代碼來自動推斷出類型:
let n = 1; // ts會自動推斷出n是number類型 n+=3 // 不報錯,由於已知類型 let arr1 = []; // 類型爲: never[] arr1.push(1); // 報錯, let arr = [1]; // 內部要有數字, 才能推斷出正確類型 arr.push(2);
let n: number|null = 0.5 < Math.random() ? 1:null; if(null !== n){ n+=3 // ts知道如今n不是null是number }
document.ontouchstart = ev=>{ // 能自動推斷出ev爲TouchEvent console.log(ev.touches); // 不報錯, TouchEvent上有touches屬性 }
typeof
就是js中的typeof
, ts會根據你代碼中出現的typeof
來自動推斷類型:
let n:number|string = 0.5 < Math.random()? 1:'1'; // 若是沒有typeof, n*=2會報錯, 提示無法推斷出當前是number類型, 不能進行乘法運算 if('number' === typeof n) { n*= 2; } else { n= '2'; }
注意: 在ts文檔中, 該部分的知識點叫作typeof類型保護, 和其餘類型推斷的內容是分開的, 被寫在高級類型/類型保護章節中.
ts會根據你代碼中出現的instanceof
來自動推斷類型:
let obj = 0.5 < Math.random() ? new String(1) : new Array(1); if(obj instanceof String){ // obj推斷爲String類型 obj+= '123' } else { // obj爲any[]類型 obj.push(123); }
注意: 在ts文檔中, 該部分的知識點叫作instanceof類型保護, 和其餘類型推斷的內容是分開的, 被寫在高級類型/類型保護章節中.
有些狀況下系統沒辦法自動推斷出正確的類型, 就須要咱們標記下, 斷言有2種語法, 一種是經過"<>", 一種經過"as", 舉例說明:
let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[] // 斷言, 告訴ts, obj爲數組 (<number[]>obj).push(1); //等價 (obj as number[]).push(1);
類型別名能夠表示不少接口表示不了的類型, 好比字面量類型(經常使用來校驗取值範圍):
type A = 'top'|'right'|'bottom'|'left'; // 表示值多是其中的任意一個 type B = 1|2|3; type C = '紅'|'綠'|'黃'; type D = 150; let a:A = 'none'; // 錯誤, A類型中沒有'none'
interface A1{ a:number; } type B = A1 | {b:string}; type C = A1 & {b:string}; // 與泛型組合 type D<T> = A1 | T[];
js中的Object.keys
你們確定都用過, 獲取對象的鍵值, ts中的keyof
和他相似, 能夠用來獲取對象類型的鍵值:
type A = keyof {a:1,b:'123'} // 'a'|'b' type B = keyof [1,2] // '1'|'2'|'push'... , 獲取到內容的同時, 還獲得了Array原型上的方法和屬性(實戰中暫時沒遇到這種需求, 瞭解便可)
能夠得到鍵值, 也能夠獲取對象類型的值的類型:
type C = A['a'] // 等於type C = 1; let c:C = 2 // 錯誤, 值只能是1
映射類型比較像修改類型的工具函數, 好比Readonly
能夠把每一個屬性都變成只讀:
type A = {a:number, b:string} type A1 = Readonly<A> // {readonly a: number;readonly b: string;}
打開node_modules/typescript/lib文件夾能夠找到lib.es5.d.ts
, 在這咱們能找到Readonly
的定義:
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
其實不是很複雜, 看了本節課前面前面的內容, 這個很好理解是吧:
T
.keyof
獲取T
上的鍵值集合.in
表示循環keyof
獲取的鍵值.readonly
標記.type A = {a:number, b:string} type A1 = Partial<A> // { a?: number; b?: string;}
type A = {a?:number, b?:string} type A1 = Required<A> // { a: number; b: string;}
type A = {a:number, b:string} type A1 = Pick<A, 'a'> // {a:number}
type A = {a:number, b:string} type A1 = Omit<A, 'a'> // {b:string}
type A1 = Record<string, string> // 等價{[k:string]:string}
type A = {a:number, b:string} type A1 = Exclude<number|string, string|number[]> // number // 兼容 type A2 = Exclude<number|string, any|number[]> // never , 由於any兼容number, 因此number被過濾掉
type A = {a:number, b:string} type A1 = Extract<number|string, string|number[]> // string
type A1 = NonNullable<number|string|null|undefined> // number|string
type A1= ReturnType<()=>number> // number
ts中類有2種類型, 靜態部分的類型和實例的類型, 因此T
若是是構造函數類型, 那麼InstanceType
能夠返回他的實例類型:
interface A{ a:HTMLElement; } interface AConstructor{ new():A; } function create (AClass:AConstructor):InstanceType<AConstructor>{ return new AClass(); }
返回類型爲元祖, 元素順序同參數順序.
interface A{ (a:number, b:string):string[]; } type A1 = Parameters<A> // [number, string]
和Parameters
相似, 只是T
這裏是構造函數類型.
interface AConstructor{ new(a:number):string[]; } type A1 = ConstructorParameters<AConstructor> // [number]
T extends U ? X : Y
用來表示類型是不肯定的, 若是U
的類型能夠表示T
, 那麼返回X
, 不然Y
. 舉幾個例子:
type A = string extends '123' ? string :'123' // '123' type B = '123' extends string ? string :123 // string
明顯string
的範圍更大, '123'
能夠被string
表示, 反之不可.
單詞自己的意思是"推斷", 實際表示在extends
條件語句中聲明待推斷的類型變量. 咱們上面介紹的映射類型中就有不少都是ts在lib.d.ts
中實現的, 好比Parameters
:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
上面聲明一個P
用來表示...args
可能的類型, 若是(...args: infer P)
能夠表示 T
, 那麼返回...args
對應的類型, 也就是函數的參數類型, 反之返回never
.
注意: 開始的T extends (...args: any) => any
用來校驗輸入的T
是不是函數, 若是不是ts會報錯, 若是直接替換成T
不會有報錯, 會一直返回never
.
接下來咱們利用infer
來實現"刪除元祖類型中第一個元素", 這經常使用於簡化函數參數, 這有一個我以前的應用
export type Tail<Tuple extends any[]> = ((...args: Tuple) => void) extends ((a: any, ...args: infer T) => void) ? T : never;
多寫多練, 很快就上手, 放幾個我用ts寫的項目當作參考, 拋磚引玉, 加油!
手勢庫: https://github.com/any86/any-...
命令式調用vue組件: https://github.com/any86/vue-...
工做中經常使用的一些代碼片斷: https://github.com/any86/usef...
一個mini的事件管理器: https://github.com/any86/any-...
上面聲明一個P
用來表示...args
可能的類型, 若是(...args: infer P)
能夠表示 T
, 那麼返回...args
對應的類型, 也就是函數的參數類型, 反之返回never
.
注意: 開始的T extends (...args: any) => any
用來校驗輸入的T
是不是函數, 若是不是ts會報錯, 若是直接替換成T
不會有報錯, 會一直返回never
.
接下來咱們利用infer
來實現"刪除元祖類型中第一個元素", 這經常使用於簡化函數參數, 這有一個我以前的應用
export type Tail<Tuple extends any[]> = ((...args: Tuple) => void) extends ((a: any, ...args: infer T) => void) ? T : never;
多寫多練, 很快就上手, 放幾個我用ts寫的項目當作參考, 拋磚引玉, 加油!
手勢庫: https://github.com/any86/any-...
命令式調用vue組件: https://github.com/any86/vue-...
工做中經常使用的一些代碼片斷: https://github.com/any86/usef...
一個mini的事件管理器: https://github.com/any86/any-...