TS 工具泛型使用

2,183 阅读4分钟

typeof -- 对象

一般我们都是先定义类型,再定义变量,使用typeof可以得到变量的类型。

const options = {
  a: 1
}
type Options = typeof options

keyof -- 枚举类型(可以理解为keyof 对象类型

首先来看keyof的定义:keyof操作符可以用来枚举出一个对象中的所有key值。
通俗来说,keyof可以取出一个对象中的所有由key值组成的枚举类型

interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof { [x: string]: Person };  // string | number

image.png image.png image.png image.png
可以看到使用let a: { [key in keyof Person]: string };可以看到keyof Person返回的枚举类型

in -- 枚举类型

in的定义是:用于遍历枚举类型
注意:in只能遍历枚举类型,不能遍历对象(keyof遍历的是对象(类型))。

故而,Partialtype Partial<T> = { [P in keyof T]?: T[P] };)中,使用的是keyof,若是改成[P in T]?: T[P]则会报错,因为由T[P]得知,T为对象,而由[P] in T得知,T只能为string | number | symbol,故而有冲突。

再看下面这个例子:

export enum IFlag {
  A = 'A',
  B = 'B',
  C = "C",
  D = "D",
}

export const OFlag: {[key in IFlag]?: string} = {
  [IFlag.A]: '情景A',
  [IFlag.B]: '情景B',
  [IFlag.C]: '情景C',
  [IFlag.D]: '情景D',
}

在项目中,使用此方式来管理页面中一些固定的键值,例如select下拉选项:

<select>
  <option value="A">情景A</option>
  <option value="B">情景B</option>
  <option value="C">情景C</option>
  <option value="D">情景D</option>
</select>

IFlag枚举出所有的值,作为接口的入参,OFlag配置页面的文案展示。 注意:IFlag需定义为字符串枚举。

Partial -- 对象类型

Partial实现源码node_modules/typescript/lib/lib.es5.d.ts

type Partial<T> = { [P in keyof T]?: T[P] };

使用keyof T拿到T中的所有的属性值,[P in keyof T]表示Pkeyof T内,T[P]取出对应属性的值,?代表属性可选。

Required --对象类型

Required实现源码node_modules/typescript/lib/lib.es5.d.ts。

type Required<T> = { [P in keyof T]-?: T[P] };

-?的作用就是把可选属性的可选性去掉,使该属性变成必选项,对应的还有+?(等价于?),作用与-?相反,是把属性变为可选项。

  • [P in keyof T]?: T[P]:可选
  • [P in keyof T]+?: T[P]:可选,等价于?
  • [P in keyof T]-?: T[P]:必选
  • [P in keyof T]: T[P]:可选的依然可选,必选的依然必选

Exclude<T, U> -- 枚举类型

T中去除TU的交集后的类型。Exclude实现源码node_modules/typescript/lib/lib.es5.d.ts。

type Exclude<T, U> = T extends U ? never : T;

例子:

type T = Exclude<1 | 2 | 3 | 4 | 5, 3 | 4>

Extract<T, U> -- 枚举类型

T中提取TU的交集类型。Extract实现源码node_modules/typescript/lib/lib.es5.d.ts。

type Extract<T, U> = T extends U ? T : never;

例子:

type T = Extract<1 | 2 | 3 | 4 | 5, 3 | 4>  // T = 3 | 4

Pick<T, K> -- 对象类型

T中提取出TK属性值相同的属性。Pick实现源码node_modules/typescript/lib/lib.es5.d.ts。

PickExtract都有从T中提取TK相交的那部分,他们的区别在于Pick作用于对象类型,返回的类型用于定义对象;Extract作用于枚举类型,返回的类型用于定义枚举类型变量。

type Pick<T, K extends keyof T> = { [P in K]: T[P]; };

例子:假设Person类型中有nameagesex属性,当我们想生成一个新的类型只支持nameage时,则可使用Pick

interface Person {
  name: string,
  age: number,
  sex: string,
}

let person: Pick<Person, 'name' | 'age'> = {
  name: '小王',
  age: 21,
}

Omit<T, K>(非内置)

从对象T中剔除keyK中的属性。
OmitTS中没有内置,Omit可以使用PickExclude实现。

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>

例子: 剔除Person中的name属性。

interface Person {
  name: string,
  age: number,
  sex: string,
}

let person: Omit<Person, 'name'> = {
  age: 18,
  sex: '男'
}

Record<K, T>

K中所有的属性的值的类型转化为T类型。Record实现源码node_modules/typescript/lib/lib.es5.d.ts。

type Record<K extends keyof any, T> = { [P in K]: T; };

例子: 将Person中的属性值的类型全部转为string类型。

interface Person {
  name: string,
  age: number,
}

let person: Record<keyof Person, string> = {
  name: '小王',
  age: '12',
}

NonNullable<T> -- 枚举类型

剔除Tnullundefined的类型。NonNullable实现源码node_modules/typescript/lib/lib.es5.d.ts。

type NonNullable<T> = T extends null | undefined ? never : T;

例子:

type T = NonNullable<string | string[] | null | undefined>; // string | string[]

ReturnType<T>

获取函数T返回值的类型。ReturnType实现源码node_modules/typescript/lib/lib.es5.d.ts。

type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;

infer R相当于声明一个变量,接收传入函数的返回值类型。 例子:

type T1 = ReturnType<() => string>; // string
type T2 = ReturnType<(s: string) => void>; // void

Conditional Types(条件类型)

条件类型测试两种类型,然后根据该测试的结果选择其中一种。表现形式为T extends U ? X : Y, 即如果类型T是类型U的子类型,则返回X类型,否则返回Y类型。

type NonNullable<T> = T extends null | undefined ? never : T;

通过一个简单的案例来进行理解,当泛型Tstring类型的时候,那么Btype B = "1",反之为type B = "2"。可以看到同样的一个类型,因为传入的泛型T不一样,结果自然而然的有了出入。

type B<T> = T extends string ? '1' : '2';


const a: B<string> = '1';
const b: B<number> = '1'; // 不能将类型"1"分配给类型"2"