Typescript内置工具类型和操作符介绍

随着 Typescript 的普及,越来越多的前端项目开始使用 Typescript,做了几个项目后发现自己对 Typescript 内置类型非常不熟悉,今天梳理下知识点介绍下 Typescript 内置工具类型和常用操作符

Partial<Type>
Required<Type>
Readonly<Type>
Record<Keys, Type>
Pick<Type, Keys>
Omit<Type, Keys>
Exclude<UnionType, ExcludedMembers>
Extract<Type, Union>
NonNullable<Type>
Parameters<Type>
ConstructorParameters<Type>
ReturnType<Type>
InstanceType<Type>
Uppercase<StringType>
Lowercase<StringType>
Capitalize<StringType>
Uncapitalize<StringType>

内置工具类型 Utility Types

Partial(部分的)

作用是将传入的属性变为可选项
实现原理

/**
 * Make all properties in T optional
 */
type Partial<T> = {
  [P in keyof T]?: T[P];
};

Example

interface Foo {
  name: string;
  age: number;
}
type B = Partial<Foo>;
//type B = {name?: string; age?: number;}

Required(必须的)

作用是将传入的属性变为必选项
实现原理

/**
 * Make all properties in T required
 */
type Required<T> = {
  [P in keyof T]-?: T[P];
};

Example

interface Foo {
  name?: string;
  age?: number;
}
type A = Required<Foo>;
// type A = {name: string; age: number;}

Readonly(只读的)

作用是让传入类型中的所有属性变成都是只读的(不能修改属性)
实现原理

/**
 * Make all properties in T readonly
 */
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

Example

interface Todo {
  title: string;
}

const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};

todo.title = "Hello"; //Cannot assign to 'title' because it is a read-only property.

Record(记录)

作用是构建一个类型,这个类型用来描述一个对象,这个对象的属性都具有相同的类型
实现原理

/**
 * Construct a type with a set of properties K of type T
 */
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

Example

export const student1: Record<string, any> = {
  name: "张三",
  age: 20,
};

Pick(摘取)

作用是选择传入类型中的部分属性组成新类型
实现原理

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Example

interface User {
  age: number;
  name: string;
  type: boolean;
}
type PickUser = Pick<User, "age" | "name">;
//type PickUser = { age: number; name: string; }

Omit(省略)

传入一个类型,和这个类型的几个属性,把传入的属性省略掉,组成一个新类型
实现原理

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Example

interface User {
  id: number;
  age: number;
  name: string;
}
type OmitUser = Omit<User, "id">;
// type OmitUser = { age: number; name: string; }

Exclude(排除)

就是将前面类型的与后面类型对比,过滤出前面独有的属性
实现原理

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

Example

const str: Exclude<"a" | "1" | "2", "a" | "y" | "z"> = "1";
//const str: '1'|'2' = '1';

Extract(取出)

与 Exclude 相反,针对联合类型,排除不同的的,取出相同的
实现原理

/**
 * Extract from T those types that are assignable to U
 */
type Extract<T, U> = T extends U ? T : never;

Example

export type PersonAttr = "name" | "age";
export type StudentAttr = "name" | "age" | "class" | "school";
const student1: Extract<StudentAttr, PersonAttr> = "name";
// const student1: "name" | "age" = "name";

NonNullable(不能为 null)

实现原理

/**
 * Exclude null and undefined from T
 */
type NonNullable<T> = T extends null | undefined ? never : T;

Example

export interface Student {
  name: string;
  age: number;
}

const student1: NonNullable<Student | undefined | null> = null;
// 不能将类型“null”分配给类型“Student”。

Parameters(参数)

获取传入函数的参数组成的类型
实现原理

/**
 * Obtain the parameters of a function type in a tuple
 */
type Parameters<T extends (...args: any) => any> = T extends (
  ...args: infer P
) => any
  ? P
  : never;

Example

export interface Student {
  name: string;
  age: number;
}

export interface StudentFunc {
  (name: string, age: number): Student;
}

const student1: Parameters<StudentFunc>;
// const student1: [name: string, age: number]

ConstructorParameters(构造参数)

获取传入构造函数的参数组成的类型
实现原理

/**
 * Obtain the parameters of a constructor function type in a tuple
 */
type ConstructorParameters<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: infer P) => any ? P : never;

Example

export interface Student {
  name: string;
  age: number;
}

export interface StudentConstructor {
  new (name: string, age: number): Student;
}

const student1: ConstructorParameters<StudentConstructor>;
// const student1: [name: string, age: number]

ReturnType(返回类型)

获取传入函数的返回类型
实现原理

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R
  ? R
  : any;

Example

export interface Student {
  name: string;
  age: number;
}

export interface StudentFunc {
  (name: string, age: number): Student;
}

const student1: ReturnType<StudentFunc> = {};
// const student1: Student

InstanceType(构造返回类型、实例类型)

获取传入构造函数的返回类型
实现原理

/**
 * Obtain the return type of a constructor function type
 */
type InstanceType<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: any) => infer R ? R : any;

Example

const Student = class {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  showInfo() {
    console.log("name: ", this.name, "age: ", this.age);
  }
};

const student1: InstanceType<typeof Student> = new Student("张三", 20);
// const student1: Student

Uppercase(大写)

实现原理

/**
 * Convert string literal type to uppercase
 */
type Uppercase<S extends string> = intrinsic;

Example

export type StudentSexType = "male" | "female";

const studentSex: Uppercase<StudentSexType> = "MALE";

Lowercase(小写)

实现原理

/**
 * Convert string literal type to lowercase
 */
type Lowercase<S extends string> = intrinsic;

Example

export type StudentSexType = "MALE" | "FEMALE";

const studentSex: Lowercase<StudentSexType> = "male";

Capitalize(首字母大写)

实现原理

/**
 * Convert first character of string literal type to uppercase
 */
type Capitalize<S extends string> = intrinsic;

Example

export type StudentSexType = "male" | "female";

const studentSex: Capitalize<StudentSexType> = "Male";

Uncapitalize(首字母小写)

实现原理

/**
 * Convert first character of string literal type to lowercase
 */
type Uncapitalize<S extends string> = intrinsic;

Example

export type StudentSexType = "MALE" | "FEMALE";

const studentSex: Uncapitalize<StudentSexType> = "mALE";

操作符

keyof 键值获取

keyof 可以用来取得一个对象接口的所有 key 值.
keyof 产生联合类型

interface Foo {
  name: string;
  age: number;
}
type T = keyof Foo;
// T -> "name" | "age"

in 遍历属性

in 只能用在类型的定义中,可以对枚举类型进行遍历

type Obj = {
  [p in T]: any;
};
// Obj -> { a: any, b: any }

typeof 代表取某个值的 type

const a: number = 3;

// 相当于: const b: number = 4
const b: typeof a = 4;

instanceof 用于检测是否在原型链上

function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
  } else {
    console.log(x.toUpperCase());
  }
}

几个特殊操作符

空值合并运算符 ??

空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。

let x = foo ?? bar();
// 等价于
let x = foo !== null && foo !== undefined ? foo : bar();

与逻辑或(||)操作符不同,逻辑或会在左操作数为 falsy 值时返回右侧操作数。也就是说,如果你使用 || 来为某些变量设置默认的值时,你可能会遇到意料之外的行为。比如为 falsy 值(’’、NaN 或 0)时。

可选链操作符 ?.

可选链的核心是允许我们写下如果碰到  null  或者  undefined,TypeScript 能立即停止运行的代码返回 undefined。
可选链耀眼的部分是使用  ?.  运算符来访问一个可选属性。

let x = foo?.bar.baz();
//等价于
let x = foo === null || foo === undefined ? undefined : foo.bar.baz();

注意:?.  与  &&  运算符行为略有不同,因为  &&  专用于 “falsy” 的值(如:空字符串、0、NaN、和  false),但是  ?.  是一个仅作用于结构上的操作符,?.  在验证有效数据如  0  或者空字符串时,它并没有使用短路验证的方式。

非空断言运算符 !

这个运算符可以用在变量名或者函数名之后,用来强调对应的元素是非 null|undefined 的

for (const key in defaults) {
  options[key] = key in partialOptions ? partialOptions[key]! : defaults[key];
}

参考文章:

  1. Utility Types
  2. TypeScript 内置类型一览