TS体操之创造新类型

62 阅读3分钟

实际代码过程中使用 TS 必然需要创建一些新的类型。 本文简要介绍了一些创建自己类型所需要的方法

泛型 采用参数的类型

keyof 运算符

keyof 运算法主要获取对象类型的 key

  • 输入是一个类型
  • 输出是这个类型所有的 key 构成的一个联合类型

typeof 类型运算符

typeof 就是获取这个变量的类型

  • 输入是☝一个变量
  • 输出是他对应的类型

type索引访问类型

索引访问类型就是将JS 当中对象的语法交给类型

  • 输入是一个类型+方括号语法
    • 方括号里面的内容必须是一个类型
  • 输出是类型会是对应索引指向的类型

方括号里面可以是一个属性的名称,也可以是一个联合类型。

值得注意的是我们可以对一个数组使用索引访问。下面联合用了 typeof 和索引访问的方法。注意他的执行顺序

const MyArray = [
    { name: "Alice", age: 15 },
    { name: "Bob", age: 23 },
    { name: "Eve", age: 38 },
];
type Person = (typeof MyArray)[number];
type Person = typeof MyArray[number];

条件类型

条件类型可以分为两类。

  • 一个是简单的判断,告诉编译器一些限制。
  • 一个则类似三目运算符,来构造新的对象
type Flatten<T> = T extends any[] ? T[number] : T;

除了用索引访问类型,我们还可以用 infer 关键字。

  • infer 关键字主要用在 extends 后面的表达式中
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
type GetRType<Type> = Type extends (...args: never[]) => infer Return? Return: never;

如果条件类型输入是个联合类型,则条件类型将应用于该联合的每个成员后再联合起来。

映射类型

说映射类型之前要先明确什么是索引签名。

  • 索引签名是一种定义事先未知的字段的 Shape 的方法。类似下面的代码
interface StringByString {  
    [key: string]: string;  
}
  • 索引签名只接受 string、number 或 symbol 作为键类型,如果想用字符串联合类型等其他类型可以用 Record

映射类型是一种泛型类型,他需要把另一种类型映射到一种新类型。

type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
};
  • readonly 和 ?分别影响可变性和可选性
  • 通过在 - 或 + 前加上前缀来删除或添加这些修饰符

映射类型可以如何修改属性名称? 假设我们知道一个对象,现在希望用 get 和 set 来修饰他的属性名。

type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};

模板文本类型

模版文本可以帮助我们根据 union literal类型生成多个literal类型 如下列代码会生成 2*3种类型

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff"|"nolocal";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;

需要注意这里代表的是一种类型,而不是变量。 这个很重要。如在理解下面的${string & keyof Type}Changed时,如果以为只是一个变量,那么就会一头雾水。实际这里是找到Type 类型所有 string 的属性,给 on 方法第一个函数添加一个xxxChanged的字符串限制。

type PropEventSource<Type> = {
    on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};

union类型

可以看到同样是泛式类型,在输入的时候判断和具体运算的时候判断是不同的

  • 具体运算判断输出的还是一个union类型
  • 输入的时候判断则会合成一体。
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;

type EventConfig<Events extends { kind: string }> = {
    [E in Events as E["kind"]]: (event: E) => void;
}
type SquareEvent = { kind: "square", x: number, y: number };
type CircleEvent = { kind: "circle", radius: number };
type Config = EventConfig<SquareEvent | CircleEvent>