Typescript 实战 --- 高级类型
Posted rogerwu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Typescript 实战 --- 高级类型相关的知识,希望对你有一定的参考价值。
类型1 & 类型2 & 类型3
interface CatInterface { run(): void } interface DogInterface { jump(): void } // 交叉类型具有所有类型的特性 let pet: CatInterface & DogInterface = { run() {}, jump() {} }
let a: number | string = 2; a = ‘hello‘; a = undefined; // 可以为其子类型 a = true; // Error: 不能将类型“true”分配给类型“string | number”
// 字符串联合类型 let x: ‘typescript‘ | ‘webpack‘ | ‘nodejs‘; x = ‘webpack‘; x = ‘hello‘; // Error: 不能将类型“"hello"”分配给类型“"typescript" | "webpack" | "nodejs"” // 数字联合类型 let y: 1 | 2 | 3; y = 3; y = 33; // Error: 不能将类型“33”分配给类型“1 | 2 | 3” let z: ‘typescript‘ | 2; z = ‘typescript‘; z = 2; z = 1; // Error: 不能将类型“1”分配给类型“"typescript" | 2”
enum Pet { Dog, Cat }; interface DogInterface { run(): void; eat(): void; } interface CatInterface { jump(): void; eat(): void; } class Dog implements DogInterface { run() {}; eat() {}; } class Cat implements CatInterface { jump() {}; eat() {}; } function getPet(pet: Pet) { // let smallPet: Dog | Cat let smallPet = pet === Pet.Dog ? new Dog() : new Cat(); // 类型不确定时,只能取公有成员 smallPet.eat(); smallPet.run(); // 类型“Dog | Cat”上不存在属性“run” smallPet.jump(); // 类型“Dog | Cat”上不存在属性“jump” return smallPet; }
// 例如:Shape是多个类型的联合类型,每个类型都具有一个公共属性kind,由此在 switch中建立了不同类型的保护区块 interface Rectangle { kind: ‘rectangle‘; width: number; height: number; } interface Square { kind: ‘square‘; size: number; } type Shape = Rectangle | Square; function area(s: Shape) { switch(s.kind) { case ‘rectangle‘: return s.width * s.height; case ‘square‘: return s.size * s.size; } }
如果又添加了一个联合类型,但是又没有在 area 函数中设定类型保护区块,会发生什么呢?
interface Rectangle { kind: ‘rectangle‘; width: number; height: number; } interface Square { kind: ‘square‘; size: number; } // 添加新的联合类型 interface Circle { kind: ‘circle‘; r: number; } type Shape = Rectangle | Square | Circle; function area(s: Shape) { switch(s.kind) { case ‘rectangle‘: return s.width * s.height; case ‘square‘: return s.size * s.size; } } console.log(area({ kind: ‘circle‘, r: 1 })); // undefined
执行程序打印出了一个结果 undefined,由于上例中并没有在 area 方法中为 Circle 指定计算面积的方法,理论上应该提示错误,而不是直接返回 undefined。
为了让编译器正确的提示错误,有两种可选方法:
(1)、为 area 方法指定返回值类型
function area(s: Shape): number { switch(s.kind) { case ‘rectangle‘: return s.width * s.height; case ‘square‘: return s.size * s.size; } }
(2)、利用never类型
// 给定一个 default 分支,通过判断 s 是不是 never 类型来提示错误。 // 如果是 never 类型,则可以在前面的分支中找到对应的执行代码; // 如果不是 never 类型,则说明前面的代码有遗漏,需要补全 function area(s: Shape): number { switch(s.kind) { case ‘rectangle‘: return s.width * s.height; case ‘square‘: return s.size * s.size; default: return ((e: never) => { throw new Error(e) })(s); // 类型“Circle”的参数不能赋给类型“never”的参数 } }
通过错误提示补全代码
function area(s: Shape): number { switch(s.kind) { case ‘rectangle‘: return s.width * s.height; case ‘square‘: return s.size * s.size; case ‘circle‘: return Math.PI * s.r ** 2 default: return ((e: never) => { throw new Error(e) })(s); } } console.log(area({ kind: ‘circle‘, r: 1 })); // 3.141592653589793
3、索引类型
使用索引类型,编译器就能够检查使用了动态属性名的代码。例如:从js对象中选取属性的子集,然后建立一个集合
let obj = { a: 1, b: 2, c: 3 } function getValues(obj: any, keys: string[]) { return keys.map(key => obj[key]) } // obj 中存在的属性 console.log(getValues(obj, [‘a‘, ‘b‘])); // [ 1, 2 ] // obj 中不存的属性,返回 undefined,而没有提示报错 console.log(getValues(obj, [‘e‘, ‘f‘])); // [ undefined, undefined ]
索引类型可以用来解决上例中的问题,在认识索引类型之前需要先了解几个概念:
(1)、索引类型查询操作符 keyof T
对于任何类型T,keyof T 的结果是 类型T的所有公共属性的字面量的联合类型
interface Person { name: string; gender: string; age: number; } let personProps: keyof Person; // ‘name‘ | ‘gender‘ | ‘age‘ console.log(personProps)
(2)、索引访问操作符 T[K]
interface Person { name: string; gender: string; age: number; } let n: Person[‘name‘]; // n 的类型是 string let a: Person[‘age‘]; // a 的类型是 number
// 1、用T来约束obj // 2、用K来约束keys数组 // 3、给K增加一个类型约束,让它继承obj的所有属性的联合类型 // 4、函数的返回值是一个数组,数组的元素的类型就是属性K对应的类型 let obj = { a: 1, b: 2, c: 3 } function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] { return keys.map(key => obj[key]) } // obj 中存在的属性 console.log(getValues(obj, [‘a‘, ‘b‘])); // [ 1, 2 ] console.log(getValues(obj, [‘e‘, ‘f‘])); // Error:不能将类型“string”分配给类型“"a" | "b" | "c"”
4、映射类型
interface Obj { a: string; b: number; c: boolean; }
4-1、同态
同态的意思是:只会作用于旧类型的属性,而不会引入新的属性
(1)、Readonly<T> 将旧类型中的每一个成员都变成只读
type ReadonlyObj = Readonly<Obj>;
(2)、Partial<T> 把旧类型中的每一个成员都变成可选的
type PartialObj = Partial<Obj>;
(3)、Pick<T, key1 | key2 | keyn> 可以抽取旧类型中的一些子集
接受两个参数:第一个是要抽取的对象,第二个是要抽取的属性的key
type PickObj = Pick<Obj, ‘a‘ | ‘c‘>;
4-2、非同态,会创建一些新的属性
(1)、Record<key1 | key2 | keyn, T>
接受两个参数:第一个参数是一些预定义的新的属性,第二个参数是一个已知的对象
type RecordObj = Record<‘x‘ | ‘y‘, Obj>;
以上是关于Typescript 实战 --- 高级类型的主要内容,如果未能解决你的问题,请参考以下文章
TypeScript学习笔记——TS类型/高级用法及实战优缺点
TypeScript学习笔记——TS类型/高级用法及实战优缺点
阿里前端大佬TypeScript学习笔记——TS类型/高级用法及实战优缺点
TypeScript高级类型入门手册:附大量代码实例(收藏!)