TypeScript 工具类型解析(从入门到入土)

141 阅读5分钟

TypeScript 工具类型解析

在 TypeScript 中,工具类型是一种强大的工具,用于创建和转换类型。本文将介绍一些常用的 TypeScript 工具类型,包括 RecordPartialRequiredReadonlyPickExcludeExtractOmitNonNullableParametersConstructorParametersReturnTypeInstanceType

这些 TypeScript 工具类型能帮助你更轻松地操作和转换类型:

  • Record: 定义对象的键值对类型。
  • Partial: 生成所有属性可选的新类型。
  • Required: 生成所有属性必需的新类型。
  • Readonly: 生成所有属性只读的新类型。
  • Pick: 从类型中选择指定属性生成新类型。
  • Omit: 从类型中排除指定属性生成新类型。
  • Exclude: 从类型中排除指定属性生成新类型。
  • Extract: 从类型中提取指定属性生成新类型。
  • NonNullable: 从类型中排除 nullundefined
  • Parameters: 获取函数的参数类型并以元组形式返回。
  • ConstructorParameters: 获取构造函数的参数类型并以元组形式返回。
  • ReturnType: 获取函数的返回值类型。
  • InstanceType: 获取构造函数的实例类型。

Record

Record 是一个很常用的工具类型,用于定义一个对象的键值对类型。

type Record<K extends string | number | symbol, T> = {
    [P in K]: T;
}

举个栗子

// 定义一个描述宠物信息的对象
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
    name:string,
    age:number,
}

type IPets = Record<petsGroup, IPetInfo>;

const animalsInfo:IPets = {
    dog:{
        name:'dogName',
        age:2
    },
    cat:{
        name:'catName',
        age:3
    },
    fish:{
        name:'fishName',
        age:5
    }
}

Partial

Partial 用于生成一个新类型,该类型与原始类型拥有相同的属性,但是所有属性都是可选的。

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

举个栗子

interface Foo {
    name: string; age?: number;
} 
type Bar = Partial<Foo>; 
// Bar 的类型为 { name?: string; age?: number | undefined; }

Required

Required 用于生成一个新类型,该类型与原始类型拥有相同的属性,但是所有属性都是必需的。

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

举个栗子

interface Foo {
    name: string
    age?: number
}
type Bar = Required<Foo>
// Bar 的类型为 { name: string; age: number; }

Readonly

Readonly 用于生成一个新类型,该类型中的所有属性都是只读的,不可修改。

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

举个栗子

interface Foo {
    name: string
    age: number
}
type Bar = Readonly<Foo>
// Bar 的类型为 { readonly name: string; readonly age: number; }

Pick

Pick 用于从一个类型中挑选出指定的属性,生成一个新类型。

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

举个栗子

interface Foo {
    name: string;
    age?: number;
    gender: string;
}

type Bar = Pick<Foo, 'age' | 'gender'>;
// Bar 的类型为 { age?: number | undefined; gender: string; }

Omit

Omit 用于从一个类型中排除指定的属性,生成一个新类型。

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

举个栗子

type Foo = {
    name: string;
    age: number;
}

type Bar = Omit<Foo, 'age'>;
// Bar 的类型为 { name: string; }

Exclude

Exclude 用于从一个类型中排除指定的属性。

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

举个栗子

type A = number | string | boolean;
type B = number | boolean;

type Foo = Exclude<A, B>;
// Foo 的类型为 string

Extract

Extract 用于从一个类型中提取指定的属性。

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

举个栗子

type A = number | string | boolean;
type B = number | boolean;

type Foo = Extract<A, B>;
// Foo 的类型为 number | boolean

NonNullable

NonNullable 用于从一个类型中排除掉 nullundefined

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

举个栗子

type t = NonNullable<'name' | undefined | null>; // t 的类型为 'name'

Parameters

Parameters 类型别名用于以元组的方式获取函数的入参类型。

Parameters<T extends (...args: any) => any>

举个栗子

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

type t = Parameters<(name: string) => any>; // type t = [string]

type t2 = Parameters<((name: string) => any)  | ((age: number) => any)>; // type t2 = [string] | [number]

这里,T extends (...args: any) => any 约束了泛型 T 必须是一个函数类型。然后,通过条件类型 T extends (...args: infer P) => any ? P : never,如果 T 是一个函数类型,就使用 infer 关键字获取其参数类型并将其命名为 P,最终返回 P,即参数类型的元组。

ConstructorParameters

ConstructorParameters 类型别名用于以元组的方式获取构造函数的入参类型。

ConstructorParameters<T extends new (...args: any) => any>

举个栗子

type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
 
type t = ConstructorParameters<(new (name: string) => any)  | (new (age: number) => any)>;
// type t = [string] | [number]

Parameters 类型别名类似,这里的泛型 T 也必须是一个构造函数类型。通过条件类型 T extends new (...args: infer P) => any ? P : never,获取构造函数参数的类型并返回。

ReturnType

ReturnType 类型别名用于获取函数返回值的类型。

ReturnType<T extends (...args: any) => any>

举个栗子

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
 
type t = ReturnType<(name: string) => string | number>;
// type t = string | number

同样,这里的泛型 T 必须是一个函数类型。通过条件类型 T extends (...args: any) => infer R ? R : any,获取函数的返回值类型并返回。

InstanceType

InstanceType 类型别名用于获取构造函数返回值的类型。

InstanceType<T extends new (...args: any) => any>

举个栗子

type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
 
type t = InstanceType<new (name: string) => {name: string, age: number}>
/* 
type h = {
    name: string;
    age: number;
}
*/

同样,这里的泛型 T 必须是一个构造函数类型。通过条件类型 T extends new (...args: any) => infer R ? R : any,获取构造函数的实例类型并返回。

总结

  1. 熟练理解内置的 TypeScript 工具类型的定义是关键。对于每种工具类型,理解其作用、语法结构以及如何使用条件类型和映射类型来实现功能是很重要的。
  2. 学会以内置工具类型为参考,能够编写自定义的工具类型,并将其运用到实际项目中。这包括理解项目中的需求,设计合适的工具类型来满足这些需求,以及在代码中正确地使用这些工具类型来实现类型安全和代码复用。

掌握这些技能可以提高代码的可维护性、可读性和可靠性,从而更有效地开发和维护 TypeScript 项目。