Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

学习 TypeScript 中的内置实用工具类型 #46

Open
jiayisheji opened this issue Dec 16, 2021 · 0 comments
Open

学习 TypeScript 中的内置实用工具类型 #46

jiayisheji opened this issue Dec 16, 2021 · 0 comments

Comments

@jiayisheji
Copy link
Owner

jiayisheji commented Dec 16, 2021

TypeScript 对于许多 Javascript 开发人员来说是难以理解的。引起麻烦的一个领域是高级类型。这个领域的一部分是 TypeScript 中内置的实用程序类型。它们可以帮助我们从现有类型中创建新的类型。在本文中,我们将了解其中一些实用工具类型如何工作以及如何使用它们。

实用工具类型简要介绍

TypeScript 为处理类型提供了一个强大的系统。这里有一些基本类型我们已经从 JavaScript 中了解。例如,数据类型如 numberstringbooleanobejctsymbolnullundefined。这并不是 TypeScript 提供的所有功能。在这些类型之上还有一些内置实用工具类型。

有时候,这些实用工具类型也是最难以理解的。当初次看到这些类型时尤为明显。好消息是,如果你理解一个重要的事情,这些类型实际上并不困难。

所有这些内置实用工具类型实际上都是简单的函数,能看这篇文章,说明你已经从 JavaScript 中知道的函数。这里的主要区别是,工具函数处理业务,这些实用工具类型,只处理类型。这些工具类型所做的就是将类型从一种类型转换为另一种类型。

这个输入是开始时使用的某种类型。还可以提供多种类型。接下来,该工具类型将转换该输入并返回适当的输出。所发生的转换类型取决于使用的实用工具类型。

Typescipt 内置了 16 个工具类型 和 4 个字符串类型(只能在字符串中使用,这里暂时不介绍它们),接下来我们就来:

Let's learn them one by one!

关于语法

TypeScript 中的所有实用工具类型都使用类似的语法。这将使我们更容易学习和记住它。如果我们尝试将这些类型看作类似于函数的东西,也会使它更容易。这通常有助于更快地理解语法,有时要快得多。关于语法。

每个实用工具类型都以类型的名称开始。这个名字总是以大写字母开头。名称后面是左尖和右尖的尖括号,小于和大于符号(<>)。括号之间是参数。这些参数是我们正在使用的类型,即输入。Typescipt 把这种语法叫泛型 GenericType<SpecificType>

仔细想想,使用实用程序类型就像调用一个函数并传递一些东西作为参数。这里的一个不同之处在于该函数始终以大写字母开头。第二个区别是,在函数名之后没有使用圆括号,而是使用尖括号。函数调用:fn(a, b)

有些类型需要一个参数,有些则需要两个或者更多。与 JavaScript 函数参数类似,这些参数由冒号(,)分割,并在尖括号之间传递。下面这个简单的例子说明了普通函数和 TypeScript 实用工具类型之间的相似性。

// 在JavaScript中调用函数
myFunction('one argument');
myFunction('one argument', 'two argument');
myFunction('one argument', 'some argument');

// 在TypeScript中使用内置类型
UtilityType<'one type'>;
UtilityType<'one type', 'two type'>;
UtilityType<'one type', 'some type'>;

关于可用性

我们将要学习的类型在 TypeScript 4.0 及以上版本中全部可用。确保你使用这个版本。否则,下面的一些类型可能无法工作,或者没有一些额外的包就无法工作。

Partial

  • 语法:Partial<Type>
  • 发布:2.1 版本
  • 实现:
type Partial<T> = {
    [P in keyof T]?: T[P];
};

创建 typeinterface 时,所有在内部定义的类型都需要作为默认值。如果我们想将某些标记为可选的,我们可以使用 ? 并将其放在属性名称之后。这将使该属性成为可选的。

// 一个可选的年龄的用户接口
interface Person {
    name: string;
    age?: number;
}
// 创建一个用户
const user: Person = {
    name: 'jack'
}

如果希望所有属性都是可选的,那么必须将所有属性都加上 ?。我们可以这样做:

// 一个可选的年龄的用户接口
interface Person {
    name?: string;
    age?: number;
}
// 创建一个用户
const user: Person = {}

它也会对与该 interface 一起工作的一切产生影响。我们可以使用的另一个选项是 Partial<Type>。该类型接受一个参数,即希望设置为可选的类型。它返回相同的类型,但其中所有先前必需的属性现在都是可选的。

// 创建一个接口
interface Person {
  name: string;
  age: number;
  jobTitle: string;
  hobbies: string[];
}

// 使用 Person 接口创建对象
const jack: Person = {
  name: 'Jack',
  age: 33,
  jobTitle: 'CTO',
  hobbies: ['reading']
}
// 这是 ok,因为 “jack”对象包含在 Person 接口中指定的所有属性。

// 使用 Person 接口创建新对象
const lucy: Person = {
  name: 'Lucy',
  age: 18,
}
// TS error: Type '{ name: string; age: number; }' is missing the following properties from type 'Person': jobTitle, hobbies

// 使用 Partial<Type> 和 Person 接口使 Person 接口中的所有属性都是可选的
const lucy: Partial<Person> = {
  name: 'Lucy',
  age: 18,
}

// 这也会有效:
const alan: Partial = {}

// Partial 之后的 Person 接口:
// interface Person {
// name?: string;
// age?: number;
// jobTitle?: string;
// hobbies?: string[];
// }

Required

  • 语法:Required<Type>
  • 发布:2.8 版本
  • 实现:
type Required<T> = {
    [P in keyof T]-?: T[P];
};

Required<Type>Partial<Type> 正好相反。如果 Partial<Type> 使所有属性都是可选的,则 Required<Type> 使它们都是必需的、不可选的。Required<Type> 的语法与 Partial<Type> 相同。唯一的区别是实用工具类型的名称。

// 创建一个接口
interface Cat {
  name: string;
  age: number;
  hairColor: string; 
  owner?: string; // <= 使“owner”属性可选
}

// 这将有效:
const suzzy: Cat = {
  name: 'Suzzy',
  age: 2,
  hairColor: 'white',
}

// 使用 Required<Type> 连同 Cat 接口使 Cat 接口中的所有属性都是必需的:
const suzzy: Required<Cat> = {
  name: 'Suzzy',
  age: 2,
  hairColor: 'white',
}
// TS error: Property 'owner' is missing in type '{ name: string; age: number; hairColor: string; }' but required in type 'Required<Cat>'.


// Required<Cat> 之后的 Cat 接口:
// interface Cat {
//   name: string;
//   age: number;
//   hairColor: string;
//   owner: string;
// }

Readonly

  • 语法:Readonly<Type>
  • 发布:2.1 版本
  • 实现:
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

有时我们希望使某些数据不可变,防止他们被修改了。Readonly<Type> 类型可以帮助我们对整个类型进行这种更改。例如,可以将接口中的所有属性设置为只读。当我们在某个对象中使用该接口,并试图改变某个对象的属性时,TypeScript 会抛出一个错误。

// 创建一个接口:
interface Book {
  title: string;
  author: string;
  numOfPages: number;
}

// 创建一个使用 Book 接口的对象
const book: Book = {
  title: 'Javascript',
  author:  'Brendan Eich',
  numOfPages: 1024
}

// 尝试改变属性
book.title = 'Typescript'
book.author = 'Anders Hejlsberg'
book.numOfPages = 2048

// 打印 book的值:
console.log(book)

// Output:
// {
//   "title": "Typescript",
//   "author": "Anders Hejlsberg",
//   "numOfPages": 2048
// }

// 将 Book 的所有属性设置为只读:
const book: Readonly<Book> = {
  title: 'Javascript',
  author: ' Brendan Eich',
  numOfPages: 1024
}
// 尝试改变属性
sevenPowers.title = 'Typescript'
// TS error:  Cannot assign to 'title' because it is a read-only property.

Record

  • 语法:Record<Keys, Type>
  • 发布:2.1 版本
  • 实现:
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

假设我们有一组属性名和属性值。也就是我们常常在 Javascript 中使用的 {key: value}。基于此数据,Record<Keys, Type> 允许我们创建键值对的记录。Record<Keys, Type> 通过将 keys 参数指定的所有属性类型与 Type 参数指定的值类型进行映射,基本上创建了一个新接口。

// 创建Table类型
type Table = Record<'width' | 'height' | 'length', number>;
// type Table is basically ('width' | 'height' | 'length' are keys, number is a value):
// interface Table {
//   width: number;
//   height: number;
//   length: number;
// }

// 根据Table类型创建新对象:
const smallTable: Table = {
  width: 50,
  height: 40,
  length: 30
}

// 根据Table类型创建新对象:
const mediumTable: Table = {
  width: 90,
  length: 80
}
// TS error: Property 'height' is missing in type '{ width: number; length: number; }' but required in type 'Table'.

// 创建类型与一些字符串键:
type PersonKeys = 'firstName' | 'lastName' | 'hairColor'

// 创建一个使用 Personkeys 类型:
type Person = Record<PersonKeys, string>
// type Person is basically (personKeys are keys, string is a value):
// interface Person {
//   firstName: string;
//   lastName: string;
//   hairColor: string;
// }

const jane: Person = {
    firstName: 'Jane',
    lastName: 'Doe',
    hairColor: 'brown'
}

const james: Person = {
    firstName: 'James',
    lastName: 'Doe',
}
// TS error: Property 'hairColor' is missing in type '{ firstName: string; lastName: string; }' but required in type 'Person'.

type Titles = 'Javascript' | 'Typescript' | 'Python'

interface Book {
  title: string;
  author: string;
}

const books: Record<Titles, Book> = {
  Javascript: {
    title: 'Javascript',
    author: 'Brendan Eich'
  },
  Typescript: {
    title: 'Typescript',
    author: 'Anders Hejlsberg'
  },
  Python: {
    title: 'Python',
    author: 'Guido Van Rossum'
  },
}

// Record<Titles, Book> 基本上相当于:
Javascript: { // <= "Javascript" 键是指定的 "Titles".
  title: string,
  author: string,
}, // <= "Javascript" 值是指定的 "Book".
Typescript: { // <= "Typescript" 键是指定的 "Titles".
  title: string,
  author: string,
}, // <= "Typescript" 值是指定的 "Book".
Python: { // <= "Python" 键是指定的 "Titles".
  title: string,
  author: string,
} // <= "Python" 值是指定的 "Book".

Pick

  • 语法:Pick<Type, Keys>
  • 发布:2.1 版本
  • 实现:
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

假设我们只想使用现有接口的一些属性。可以做的一件事是创建新接口,只使用这些属性。另一个选项是使用 Pick<Type, Keys>Pick 类型允许我们获取现有类型(type),并从中只选择一些特定的键(keys),而忽略其余的。这个类型和 lodash.pick 工具函数功能一样,如果你理解这个函数,那么对于这个类型理解就很轻松了。

// 创建一个 Beverage  接口:
interface Beverage {
  name: string;
  taste: string;
  color: string;
  temperature: number;
  additives: string[] | [];
}

// 创建一个仅使用“name”,“taste”和“color”属性 Beverage 类型:
type SimpleBeverage = Pick<Beverage, 'name' | 'taste' | 'color'>

// 把 Basically  转化成:
// interface SimpleBeverage {
//   name: string;
//   taste: string;
//   color: string;
// }

// 使用 SimpleBeverage 类型创建新对象:
const water: SimpleBeverage = {
  name: 'Water',
  taste: 'bland',
  color: 'transparent',
}

Omit

  • 语法:Omit<Type, Keys>
  • 发布:3.5 版本
  • 实现:
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Omit<Type, Keys> 基本上是一个相反的 Pick<Type, Keys>。我们指定某些类型作为 type 的参数,但不是选择我们想要的属性,而是选择希望从现有类型中省略的属性。这个类型和 lodash.omit 工具函数功能一样,如果你理解这个函数,那么对于这个类型理解就很轻松了。

// 创建一个 Car 接口:
interface Car {
  model: string;
  bodyType: string;
  numOfWheels: number;
  numOfSeats: number;
  color: string;
}

// 基于 Car 接口创建 Boat 类型,但省略 “numOfWheels” 和 “bodyType” 属性
type Boat = Omit<Car, 'numOfWheels' | 'bodyType'>;

// 把 Boat 转化成:
// interface Boat {
//   model: string;
//   numOfSeats: number;
//   color: string;
// }

// 基于 Car 创建新对象:
const tesla: Car = {
  model: 'S',
  bodyType: 'sedan',
  numOfWheels: 4,
  numOfSeats: 5,
  color: 'grey',
}

// 基于 Boat 创建新对象:
const mosaic: Boat = {
  model: 'Mosaic',
  numOfSeats: 6,
  color: 'white'
}

Exclude

  • 语法:Exclude<Type, ExcludedUnion>
  • 发布:2.8 版本
  • 实现:
type Exclude<T, U> = T extends U ? never : T;

初次使用 Exclude<Type, ExcludedUnion> 可能有点令人困惑。这个实用工具类型所做的是,用于从类型 Type 中取出不在 ExcludedUnion 类型中的成员。

// 创建 Colors 类型:
type Colors = 'white' | 'blue' | 'black' | 'red' | 'orange' | 'grey' | 'purple';

type ColorsWarm = Exclude<Colors, 'white' | 'blue' | 'black' | 'grey'>;
// 把 ColorsWarm 转化成:
// type ColorsWarm = "red" | "orange" | "purple";

type ColorsCold = Exclude<Colors, 'red' | 'orange' | 'purple'>;
// 把 ColorsCold 转化成:
// type ColorsCold = "white" | "blue" | "black" | "grey"

// 创建 varmColor:
const varmColor: ColorsWarm = 'red'

// 创建 coldColor:
const coldColor: ColorsCold = 'blue'

// 尝试混合:
const coldColorTwp: ColorsCold = 'red'
// TS error: Type '"red"' is not assignable to type 'ColorsCold'.

Extract

  • 语法:Extract<Type, Union>
  • 发布:2.8 版本
  • 实现:
type Extract<T, U> = T extends U ? T : never;

Extract<Type, Union> 类型执行与 Exclude<Type, ExcludedUnion> 类型相反的操作。用于从类型 Type 中取出可分配给 Union 类型的成员。有点类似集合里的交集概念。使用 Extract 之后,返回 TypeUnion 交集。

type Food = 'banana' | 'pear' | 'spinach' | 'apple' | 'lettuce' | 'broccoli' | 'avocado';

type Fruit= Extract<Food, 'banana' | 'pear' | 'apple'>;
// 把 Fruit 转换成:
// type Fruit = "banana" | "pear" | "apple";

type Vegetable = Extract<Food, 'spinach' | 'lettuce' | 'broccoli' | 'avocado'>;
// 把 Vegetable 转换成:
// type Vegetable = "spinach" | "lettuce" | "broccoli" | "avocado";

// 创建 someFruit:
const someFruit: Fruit = 'pear'

// 创建 someVegetable:
const someVegetable: Vegetable = 'lettuce'

// 尝试混合:
const notReallyAFruit: Fruit = 'avocado'
// TS error: Type '"avocado"' is not assignable to type 'Fruit'.

NonNullable

  • 语法:NonNullable<Type>
  • 发布:2.8 版本
  • 实现:
type NonNullable<T> = T extends null | undefined ? never : T;

NonNullable 实用工具类型的工作原理与 Exclude 类似。它接受指定的某种类型,并返回该类型,但不包括所有 nullundefined 类型。

// 创建类型:
type prop = string | number | string[] | number[] | null | undefined;

// 基于以前的类型创建新类型,不包括 null 和 undefined:
type validProp = NonNullable<prop>
// 把 validProp 转换成:
// type validProp = string | number | string[] | number[]

// 这是有效的:
let use1: validProp = 'Jack'

let use2: validProp = null
// TS error: Type 'null' is not assignable to type 'validProp'.

Parameters

  • 语法:Parameters<Type>
  • 发布:3.1 版本
  • 实现:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

Parameters 类型返回一个 Tuple 类型,其中包含作为 Type 传递的形参函数的类型。这些参数的返回顺序与它们在函数中出现的顺序相同。注意,Type 参数,对于 this 和以下类型,是一个函数 ((…args) =>type),而不是一个类型,比如 string

// 声明函数类型:
declare function myFunc(num1: number, num2: number): number;

// 使用 Parameter<type> 从 myFunc 函数的参数创建新的 Tuple 类型:
type myFuncParams = Parameters<typeof myFunc>;
// 把 myFuncParams 转换成:
// type myFuncParams = [num1: number, num2: number];

//  这是有效的:
let someNumbers: myFuncParams = [13, 15];

let someMix: myFuncParams = [9, 'Hello'];
// TS error: Type 'string' is not assignable to type 'number'.

// 使用 Parameter<type> 从函数的参数创建新的 Tuple 类型:
type StringOnlyParams = Parameters<(foo: string, fizz: string) => void>;
// 把 StringOnlyParams 转换成:
// type StringOnlyParams = [foo: string, fizz: string];

//  这是有效的:
let validNamesParams: StringOnlyParams = ['Jill', 'Sandy'];

let invalidNamesParams: StringOnlyParams = [false, true];
// TS error: Type 'boolean' is not assignable to type 'string'.

ConstructorParameters

  • 语法:ConstructorParameters<Type>
  • 发布:3.1 版本
  • 实现:
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;

ConstructorParameters 类型与 Parameters 类型非常相似。这两者之间的区别在于, Parameters 从函数参数中获取类型,而ConstructorParameters 从作为 Type 参数传递的构造函数(Constructor)中获取类型。

// 创建一个 class:
class Human {
  public name
  public age
  public gender

  constructor(name: string, age: number, gender: string) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
}

// 创建基于 Human 构造函数类型:
type HumanTypes = ConstructorParameters<typeof Human>
// 把 HumanTypes 转换成:
// type HumanTypes = [name: string, age: number, gender: string]

const joe: HumanTypes = ['Joe', 33, 'male']
const sandra: HumanTypes = ['Sandra', 41, 'female']
const thomas: HumanTypes = ['Thomas', 51]
// TS error: Type '[string, number]' is not assignable to type '[name: string, age: number, gender: string]'.
// Source has 2 element(s) but target requires 3.

// 创建基于 String 构造函数类型:
type StringType = ConstructorParameters<StringConstructor>
// 把 StringType 转换成:
// type StringType = [value?: any]

ReturnType

  • 语法:ReturnType<Type>
  • 发布:2.8 版本
  • 实现:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

ReturnType 也类似于 Parameters 类型。这里的不同之处在于,ReturnType 提取作为 type 参数传递的函数的返回类型。

// 声明函数类型:
declare function myFunc(name: string): string;

// 使用 ReturnType<Type> 从 myFunc 类型中提取返回类型:
type MyFuncReturnType = ReturnType<typeof myFunc>;
// 把 MyFuncReturnType 转换成:
// type MyFuncReturnType = string;

// 这是有效的:
let name1: MyFuncReturnType = 'Types';

// 这是有效的:
let name2: MyFuncReturnType = 42;
// TS error: Type 'number' is not assignable to type 'string'.

type MyReturnTypeBoolean = ReturnType<() => boolean>
// 把 MyReturnTypeBoolean 转换成:
// type MyReturnTypeBoolean = boolean;

type MyReturnTypeStringArr = ReturnType<(num: number) => string[]>;
// 把 MyReturnTypeStringArr 转换成:
// type MyReturnTypeStringArr = string[];

type MyReturnTypeVoid = ReturnType<(num: number, word: string) => void>;
// 把 MyReturnTypeVoid 转换成:
// type MyReturnTypeVoid = void;

InstanceType

  • 语法:InstanceType<Type>
  • 发布:2.8 版本
  • 实现:
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

Instancetype 有点复杂。它所做的就是从作为 Type 参数传递的构造函数的实例类型创建一个新类型。如果使用一个常规类来处理类,则可能不需要此实用工具类型。可以只使用类名来获取所需的实例类型。

// 创建一个 class:
class Dog {
  name = 'Sam'
  age = 1
}

type DogInstanceType = InstanceType<typeof Dog>
// 把 DogInstanceType 转换成:
// type DogInstanceType = Dog

// 类似于使用 class 声明:
type DogType = Dog
// 把 DogType 转换成:
// type DogType = Dog

ThisParameterType

  • 语法:ThisParameterType<Type>
  • 发布:3.3 版本
  • 实现:
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

ThisParameterType 提取了作为 Type 参数传递的函数的 this 形参的使用类型。如果函数没有这个参数,实用工具类型将返回unknown

// 创建一个使用 this 参数函数:
function capitalize(this: String) {
  return this[0].toUpperCase + this.substring(1).toLowerCase()
}

// 创建基于 this 参数的 capitalize函数类型:
type CapitalizeStringType = ThisParameterType<typeof capitalize>
// 把 CapitalizeStringType 转换成:
// type CapitalizeStringType = String

// 创建一个不使用 this 参数函数:
function sayHi(name: string) {
  return `Hello, ${name}.`
}

// 创建基于不带 this 参数的 printUnknown 函数类型:
type SayHiType = ThisParameterType<typeof sayHi>
// 把 SayHiType 转换成:
// type SayHiType = unknown

OmitThisParameter

  • 语法:OmitThisParameter<Type>
  • 发布:3.3 版本
  • 实现:
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;

OmitThisParameter 实用类型执行与前面类型相反的操作。它通过 Type 接受一个函数类型作为参数,并返回不带 this 形参的函数类型。

// 创建一个使用 this 参数函数:
function capitalize(this: String) {
  return this[0].toUpperCase + this.substring(1).toLowerCase()
}

// 根据 capitalize 函数创建类型:
type CapitalizeType = OmitThisParameter<typeof capitalize>
// 把 CapitalizeStringType 转换成:
// type CapitalizeType = () => string


// 创建一个不使用 this 参数函数:
function sayHi(name: string) {
  return `Hello, ${name}.`
}

// 根据 Sayhi 函数创建类型:
type SayHiType = OmitThisParameter<typeof sayHi>
// 把 SayHiType 转换成:
// type SayHiType = (name: string) => string

ThisType

  • 语法:ThisType<Type>
  • 发布:2.3 版本
  • 实现:
interface ThisType<T> { }

ThisType 实用工具类型允许显式地设置 this 上下文。可以使用它为整个对象字面量或仅为单个函数设置此值。在尝试此操作之前,请确保启用了编译器标志 --noImplicitThis

// 创建 User 对象接口:
interface User {
    username: string;
    email: string;
    isActivated: boolean;
    printUserName(): string;
    printEmail(): string;
    printStatus(): boolean;
}

// 创建用户对象,并将 ThisType 设置为 user interface:
const userObj: ThisType<User> = {
  username: 'Jiayi',
  email: '[email protected]',
  isActivated: false,
  printUserName() {
    return this.username;
  },
  printEmail() {
    return this.email;
  },
  printStatus() {
    return this.isActivated;
  }
}

联合工具类型

TypeScript 内置实用工具类型的一个好处是,我们可以自由组合它们。可以将一种实用工具类型与另一种实用工具类型组合。还可以将一种实用工具类型与其他类型组合。例如,可以将类型与联合或交集类型组合。

// 创建 User 接口:
interface User {
  username: string;
  password: string;
}

// 创建 SuperUser 接口:
interface SuperUser {
  clearanceLevel: string;
  accesses: string[];
}

// 结合 User 和 SuperUser 创建 RegularUser 类型
// 让 User 属性必需的和  SuperUser 属性可选的:
type RegularUser = Required<User> & Partial<SuperUser>

// 这是有效的:
const jack: RegularUser = {
  username: 'Jack',
  password: 'some_secret_password_unlike_123456',
}

// 这是有效的:
const jason: RegularUser = {
  username: 'Jason',
  password: 'foo_bar_usually-doesnt_work-that_well',
  clearanceLevel: 'A'
}

// 这将抛出异常:
const jim: RegularUser = {
  username: 'Jim'
}
// TS error: Type '{ username: string; }' is not assignable to type 'RegularUser'.
// Property 'password' is missing in type '{ username: string; }' but required in type 'Required<User>'

扩展内置实用工具类型

虽然上面的内置实用工具类型令人惊叹,但它们并没有涵盖所有的用例,这就是提供更多实用工具的库填补空白的地方。此类库的一个很好的例子是 type-fest,它提供了更多的实用程序。

说在最后

在本文中,我们学习了 Typescript 实用工具类型,以及它们如何帮助我们从现有的类型中自动创建类型,而不会导致重复,从而无需保持相关类型的同步。我们列举一些内置的实用工具类型,我认为它们在我作为开发人员的日常工作中特别有用。在此基础上,我们推荐了 type-fest,这是一个包含许多扩展内置类型的实用程序类型的库。

今天就到这里吧,伙计们,玩得开心,祝你好运。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant