// 创建类型:
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。
TypeScript 对于许多 Javascript 开发人员来说是难以理解的。引起麻烦的一个领域是高级类型。这个领域的一部分是 TypeScript 中内置的实用程序类型。它们可以帮助我们从现有类型中创建新的类型。在本文中,我们将了解其中一些实用工具类型如何工作以及如何使用它们。
实用工具类型简要介绍
TypeScript 为处理类型提供了一个强大的系统。这里有一些基本类型我们已经从 JavaScript 中了解。例如,数据类型如
number
,string
,boolean
,obejct
,symbol
,null
,undefined
。这并不是 TypeScript 提供的所有功能。在这些类型之上还有一些内置实用工具类型。有时候,这些实用工具类型也是最难以理解的。当初次看到这些类型时尤为明显。好消息是,如果你理解一个重要的事情,这些类型实际上并不困难。
所有这些内置实用工具类型实际上都是简单的函数,能看这篇文章,说明你已经从 JavaScript 中知道的函数。这里的主要区别是,工具函数处理业务,这些实用工具类型,只处理类型。这些工具类型所做的就是将类型从一种类型转换为另一种类型。
这个输入是开始时使用的某种类型。还可以提供多种类型。接下来,该工具类型将转换该输入并返回适当的输出。所发生的转换类型取决于使用的实用工具类型。
Typescipt 内置了 16 个工具类型 和 4 个字符串类型(只能在字符串中使用,这里暂时不介绍它们),接下来我们就来:
Let's learn them one by one!
关于语法
TypeScript 中的所有实用工具类型都使用类似的语法。这将使我们更容易学习和记住它。如果我们尝试将这些类型看作类似于函数的东西,也会使它更容易。这通常有助于更快地理解语法,有时要快得多。关于语法。
每个实用工具类型都以类型的名称开始。这个名字总是以大写字母开头。名称后面是左尖和右尖的尖括号,小于和大于符号(<>)。括号之间是参数。这些参数是我们正在使用的类型,即输入。Typescipt 把这种语法叫泛型
GenericType<SpecificType>
。仔细想想,使用实用程序类型就像调用一个函数并传递一些东西作为参数。这里的一个不同之处在于该函数始终以大写字母开头。第二个区别是,在函数名之后没有使用圆括号,而是使用尖括号。函数调用:
fn(a, b)
有些类型需要一个参数,有些则需要两个或者更多。与 JavaScript 函数参数类似,这些参数由冒号(,)分割,并在尖括号之间传递。下面这个简单的例子说明了普通函数和 TypeScript 实用工具类型之间的相似性。
关于可用性
我们将要学习的类型在 TypeScript 4.0 及以上版本中全部可用。确保你使用这个版本。否则,下面的一些类型可能无法工作,或者没有一些额外的包就无法工作。
Partial
Partial<Type>
创建
type
或interface
时,所有在内部定义的类型都需要作为默认值。如果我们想将某些标记为可选的,我们可以使用?
并将其放在属性名称之后。这将使该属性成为可选的。如果希望所有属性都是可选的,那么必须将所有属性都加上
?
。我们可以这样做:它也会对与该
interface
一起工作的一切产生影响。我们可以使用的另一个选项是Partial<Type>
。该类型接受一个参数,即希望设置为可选的类型。它返回相同的类型,但其中所有先前必需的属性现在都是可选的。// 这也会有效: const alan: Partial = {}
// Partial 之后的 Person 接口:
// interface Person {
// name?: string;
// age?: number;
// jobTitle?: string;
// hobbies?: string[];
// }
Required
Required<Type>
Required<Type>
与Partial<Type>
正好相反。如果Partial<Type>
使所有属性都是可选的,则Required<Type>
使它们都是必需的、不可选的。Required<Type>
的语法与Partial<Type>
相同。唯一的区别是实用工具类型的名称。Readonly
Readonly<Type>
有时我们希望使某些数据不可变,防止他们被修改了。
Readonly<Type>
类型可以帮助我们对整个类型进行这种更改。例如,可以将接口中的所有属性设置为只读。当我们在某个对象中使用该接口,并试图改变某个对象的属性时,TypeScript 会抛出一个错误。Record
Record<Keys, Type>
假设我们有一组属性名和属性值。也就是我们常常在 Javascript 中使用的
{key: value}
。基于此数据,Record<Keys, Type>
允许我们创建键值对的记录。Record<Keys, Type>
通过将keys
参数指定的所有属性类型与Type
参数指定的值类型进行映射,基本上创建了一个新接口。Pick
Pick<Type, Keys>
假设我们只想使用现有接口的一些属性。可以做的一件事是创建新接口,只使用这些属性。另一个选项是使用
Pick<Type, Keys>
。Pick
类型允许我们获取现有类型(type),并从中只选择一些特定的键(keys),而忽略其余的。这个类型和 lodash.pick 工具函数功能一样,如果你理解这个函数,那么对于这个类型理解就很轻松了。Omit
Omit<Type, Keys>
Omit<Type, Keys>
基本上是一个相反的Pick<Type, Keys>
。我们指定某些类型作为type
的参数,但不是选择我们想要的属性,而是选择希望从现有类型中省略的属性。这个类型和 lodash.omit 工具函数功能一样,如果你理解这个函数,那么对于这个类型理解就很轻松了。Exclude
Exclude<Type, ExcludedUnion>
初次使用
Exclude<Type, ExcludedUnion>
可能有点令人困惑。这个实用工具类型所做的是,用于从类型Type
中取出不在ExcludedUnion
类型中的成员。Extract
Extract<Type, Union>
Extract<Type, Union>
类型执行与Exclude<Type, ExcludedUnion>
类型相反的操作。用于从类型Type
中取出可分配给Union
类型的成员。有点类似集合里的交集概念。使用Extract
之后,返回Type
和Union
交集。NonNullable
NonNullable<Type>
NonNullable
实用工具类型的工作原理与Exclude
类似。它接受指定的某种类型,并返回该类型,但不包括所有null
和undefined
类型。Parameters
Parameters<Type>
Parameters
类型返回一个 Tuple 类型,其中包含作为Type
传递的形参函数的类型。这些参数的返回顺序与它们在函数中出现的顺序相同。注意,Type
参数,对于this
和以下类型,是一个函数((…args) =>type)
,而不是一个类型,比如string
。ConstructorParameters
ConstructorParameters<Type>
ConstructorParameters
类型与Parameters
类型非常相似。这两者之间的区别在于,Parameters
从函数参数中获取类型,而ConstructorParameters
从作为Type
参数传递的构造函数(Constructor)中获取类型。ReturnType
ReturnType<Type>
ReturnType
也类似于Parameters
类型。这里的不同之处在于,ReturnType
提取作为type
参数传递的函数的返回类型。InstanceType
InstanceType<Type>
Instancetype
有点复杂。它所做的就是从作为Type
参数传递的构造函数的实例类型创建一个新类型。如果使用一个常规类来处理类,则可能不需要此实用工具类型。可以只使用类名来获取所需的实例类型。ThisParameterType
ThisParameterType<Type>
ThisParameterType
提取了作为Type
参数传递的函数的this
形参的使用类型。如果函数没有这个参数,实用工具类型将返回unknown
。OmitThisParameter
OmitThisParameter<Type>
OmitThisParameter
实用类型执行与前面类型相反的操作。它通过Type
接受一个函数类型作为参数,并返回不带this
形参的函数类型。ThisType
ThisType<Type>
ThisType
实用工具类型允许显式地设置this
上下文。可以使用它为整个对象字面量或仅为单个函数设置此值。在尝试此操作之前,请确保启用了编译器标志--noImplicitThis
。联合工具类型
TypeScript 内置实用工具类型的一个好处是,我们可以自由组合它们。可以将一种实用工具类型与另一种实用工具类型组合。还可以将一种实用工具类型与其他类型组合。例如,可以将类型与联合或交集类型组合。
扩展内置实用工具类型
虽然上面的内置实用工具类型令人惊叹,但它们并没有涵盖所有的用例,这就是提供更多实用工具的库填补空白的地方。此类库的一个很好的例子是 type-fest,它提供了更多的实用程序。
说在最后
在本文中,我们学习了 Typescript 实用工具类型,以及它们如何帮助我们从现有的类型中自动创建类型,而不会导致重复,从而无需保持相关类型的同步。我们列举一些内置的实用工具类型,我认为它们在我作为开发人员的日常工作中特别有用。在此基础上,我们推荐了
type-fest
,这是一个包含许多扩展内置类型的实用程序类型的库。今天就到这里吧,伙计们,玩得开心,祝你好运。