索引类型

  1. let adObj = {
  2. a: 1,
  3. b: 2,
  4. c: 3,
  5. };
  6. // 获取对象中的指定属性的值集合
  7. function getValues(adObj: any, keys: string[]) {
  8. return keys.map((key) => adObj[key]);
  9. }
  10. console.log(getValues(adObj, ["a", "b"])); // [1, 2]
  11. console.log(getValues(adObj, ["e", "f"])); // [undefined, undefined]

虽然obj中并不包含e, f属性,但TS编译器并未报错 此时使用TS索引类型,对这种模式做类型约束

概念

索引类型的查询操作符

keyof T :表示类型T的所有公共属性的字面量的联合类型

  1. interface obj {
  2. a: number;
  3. b: string;
  4. }
  5. let key: keyof obj;

image.png
索引访问操作符

T[K] :表示对象T的属性K所代表的类型

  1. interface obj {
  2. a: number;
  3. b: string;
  4. }
  5. let value: obj["a"];

image.png
泛型约束

T extends U :表示泛型变量可以通过继承某个类型,获得某些属性

索引类型

约束目的:从 obj 对象中抽取的属性数组 keys 中的元素,一定得是 obj 对象中的属性 思路:

  1. 定义泛型变量T, K,分别约束obj对象和keys数组
  2. 为K增加一个泛型约束,使K继承Obj的所有属性的联合类型, 即K extends keyof T
  3. 此时,函数返回值类型-数组的元素类型,就是属性K对应的数组,即T[K][]
  1. let adObj = {
  2. a: 1,
  3. b: 2,
  4. c: 3,
  5. };
  6. function getValues<T, K extends keyof T>(adObj: T, keys: K[]): T[K][] {
  7. return keys.map((key) => adObj[key]);
  8. }
  9. console.log(getValues(adObj, ["a", "b"])); // [1, 2]
  10. console.log(getValues(adObj, ["e", "f"]));

image.png

索引类型可以约束对象属性的查询和访问 配合泛型约束能够建立对象,对象属性,属性值之间的约束关系

映射类型

TS允许将一个类型映射成另外一个类型

  1. // 定义接口 Obj
  2. interface Obj {
  3. a: string;
  4. b: number;
  5. c: boolean;
  6. }
  7. // 使用类型别名定义类型ReadonlyObj
  8. type ReadonlyObj = Readonly<Obj>; // Readonly是TS内置的泛型接口

ReadonlyObj与Obj成员完全相同,区别是ReadonlyObj中的成员属性均为只读
image.png

Readonly的实现原理

Readonly是一个可索引类型的泛型接口

  • 索引签名为P in keyof T :

其中 keyof T就是一个一个索引类型的查询操作符,表示类型T所有属性的联合类型

  • P in :

相当于执行了一个 for in 操作,会把变量P依次绑定到T的所有属性上

  • 索引签名的返回值就是一个索引访问操作符 : T[P] 这里代表属性P所指定的类型
  • 最后再加上Readonly就把所有的属性变成了只读,这就是 Readonly 的实现原理

其他的映射类型

可选和只读映射类型的实现几乎一样, 只读属性变为可选

  1. type PartialObj = Partial<Obj>; // 可选

image.png

抽取接口Obj中的属性a和b,形成新类型

  1. type PickObj = Pick<Obj, "a" | "b">;

image.png

Pick映射类型的实现原理

Pick映射类型有两个参数: 第一个参数T,表示要抽取的目标对象 第二个参数K,具有一个约束:K一定要来自T所有属性字面量的联合类型,即映射得到的新类型的属性一定要从K中选取 以上三种映射类型官方称为同态,意思是只作用于obj属性而不会引入新的属性

引入新属性的非同态映射类型

第一个参数是预定义的新属性,如x,y 第二个参数就是已知类型

  1. type RecordObj = Record<"x" | "y", Obj>;

image.png
映射出的新类型所具有的属性由Record的第一个属性指定,而这些属性类型为第二个参数指定的已知类型,这种类型就是一个非同态的类型
映射类型本质上是一种预先定义的泛型接口,通常还会结合索引类型,获取对象的属性和属性值,从而将一个对象映射为我们想要的结构

条件类型

条件类型是一种由条件表达式所决定的类型 条件类型使类型具有了不唯一性,同样增加了语言的灵活性

声明: T extends U ? X : Y 若类型T可被赋值给类型U,那么结果类型就是X类型,否则就是Y类型

  1. type TypeName<T> =
  2. T extends string ? "string" :
  3. T extends number ? "number" :
  4. T extends boolean ? "boolean" :
  5. T extends undefined ? "undefined" :
  6. T extends Function ? "Function" : "object";
  7. type T1 = TypeName<string>;
  8. type T2 = TypeName<string[]>;

image.png
image.png

分步式条件类型

当类型T为联合类型时: T为类型A和类型B的联合类型,结果类型会变成多个条件类型的联合类型 (A | B) extends U ? X : Y

可以将A和B进行拆解: (A extends U ? X : Y) | (B extends U ? X : Y)

  1. type TypeName<T> =
  2. T extends string ? "string" :
  3. T extends number ? "number" :
  4. T extends boolean ? "boolean" :
  5. T extends undefined ? "undefined" :
  6. T extends Function ? "Function" : "object";
  7. type T3 = TypeName<string | string[]>;

image.png

分步式条件类型的应用

  1. // 如果T可以被赋值给U,结果类型为never类型,否则为T类型
  2. type Diff<T, U> = T extends U ? never : T;
  3. type T4 = Diff<"a" | "b" | "c", "a" | "e">;
  4. // 拆解分析:
  5. // Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e">
  6. // never | "b" | "c"
  7. // "b" | "c"

image.png

先判断a是否可以被赋值给这个字面量联合类型’a’ | ‘e’,答案是可以的,所以返回never 继续,因为b不可以被赋值给字面量联合类型’a’ | ‘e’,所以返回b 继续,c不可以被赋值给’a’ | ‘e’,所以返回c 最后,never和b,c的联合类型为’b’ | ‘c’

Diff类型作用:可以从类型T中过滤掉可以被赋值给类型U的类型
也可以实现从类型T中移除不需要的类型,如undefined和null

定义一个NotNull,从T中过滤掉undefined和null

  1. // Diff扩展:从T中过滤掉undefined和null
  2. type NotNull<T> = Diff<T, undefined | null>;
  3. // 过滤掉undefined和null,T5的类型就变成了string和number
  4. type T5 = NotNull<string | number | undefined | null>;

image.png

  1. // Diff 的内置类型叫做 Exclude<T, U>
  2. // NotNull 的内置类型叫做NonNullable<T>
  3. // Extract<T, u>
  4. // Extract 和 Exclude 相反
  5. // Exclude 作用是从类型T中过滤掉可以赋值给类型U的类型
  6. // Extract 作用是可以从类型T中抽取出可以赋值给U的类型
  7. type T6 = Extract<"a" | "b" | "c", "a" | "e">;
  8. type T7 = Exclude<"a" | "b" | "c", "a" | "e">;

image.png
image.png

  1. // ReturnType<T>:可获取函数返回值类型
  2. type T8 = ReturnType<() => string>;

image.png

  1. // 源码
  2. /**
  3. * Obtain the return type of a function type
  4. */
  5. type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
  6. // 分析
  7. // T extends (...args: any) => any:
  8. // ReturnType要求参数T可以赋值给一个函数,这个函数有任意的参数,返回值类型也是任意的
  9. // 由于函数返回值类型不确定,这里使用了infer关键字,表示待推断,延迟推断,需要根据实际的情况确定
  10. // infer R ? R : any:
  11. // 如果实际类型是R,那么结果类型就是R,否则返回值类型就是any