第二课, 基础类型和入门高级类型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-...