Appearance
TS提供了一些工具类型,便于通用类型转换。这些类型都是全局的。
0️⃣ ⭐ Awaited<Type>
这种类型用于对 async 函数中的await,或 Promises 中的 then() 方法等操作进行建模 - 具体讲,它们会递归解包Promises。
🌰
typescript
// type A = string
type A = Awaited<Promise<string>>
// 递归解包Promise 😎
// type B = number
type B = Awaited<Promise<Promise<number>>>
// type C = boolean | number
type C = Awaited<boolean | Promise<number>>TIP
typescript
// 如果 T 是 null | undefined,
// 直接返回 null | undefined
// 否则,判断 T是否是一个对象,并且是一个 Thenable对象(即满足Promise协议)
// 如果满足,则判断 F 是否是一个函数
// 如果是一个函数,则进行递归解包
// 否则啥也不做
// 如果不是一个函数,则直接返回 T类型(即只有一层Promise包裹)
type Awaited<T> =
T extends null | undefined
? T
: T extends object & { then(onfulfilled: infer F): any }
? F extends (value: V, ...args: any) => any
? Awaited<V>
: never
: T1️⃣ Partial<Type>
将所有的 Type 都设置为可选。这个工具类将返回一个类型,表示给定类型的所有子集。
🌰
typescript
interface Todo {
title: string;
description: string;
}
// fieldsToUpdate 为 Todo 的任意子集
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate }
}
const todo1 = {
title: 'AA',
description: 'BB'
}
const todo2 = updateTodo(todo1, {
description: 'CC'
})TIP
typescript
// 🤩 这里的 `?` 表示让每个属性都变为可选类型
type Partial<T> = {
[P in keyof T]?: T[P] | undefined;
}2️⃣ Required<Type>
将 Type 的所有属性都设置为必须的。它是上面 Partial 的反操作
🌰
typescript
interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 }
const obj2: Require<Props> = {a: 5}
// ❌ '{a: numbner;}' 类型中缺少属性 'b',它在类型 'Required<Props>' 中是必需的TIP
typescript
// 🤩 这里的 `-?` 表示去掉可选类型
type Require<T> = {
[P in keyof T]-?: T[P];
}3️⃣ Readonly<Type>
将 Type 所有属性设置为只读,这意味着属性不能重新赋值。
🌰
typescript
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: '我是只读属性'
}
todo.title = '再赋值就报错'
// ❌ 不能给 `title` 赋值,因为它是一个只读属性这个工具对运行时赋值会失败很有用(比如:当尝试对一个 冻结对象 进行重赋值)。
Object.freeze:
typescript
function freeze<Type>(obj: Type): Readonly<Type>TIP
typescript
// 即遍历T中的所有属性,然后添加 readonly 修饰
type ReadOnly<T> = {
readonly [P in keyof T]: T[P];
}4️⃣ ⭐ Record<Keys, Type>
构造一个属性键为 Keys,属性值为 Type的 对象类型。这个工具用于将一个类型的属性映射为另一个类型。
🌰
typescript
interface CatInfo {
age: number;
breed: string;
}
type CatName = 'miffy' | 'boris' | 'mordred'
// 进行映射
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: 'Persian' },
boris: { age: 5, breed: 'Maine Coon' },
mordred: { age: 4, breed: 'British Shorthair' },
}
cats.boris
//💡 const cats: Record<CatName, CatInfo>TIP
typescript
// string | number | symbol 是对象允许的合法键类型
type Record<K extends string | number | symbol, T> = {
[P in K]: T;
}5️⃣ ⭐ Pick<Type, Keys>
通过从 Type 中挑选一组属性 Keys(字符串字面量或字面量联合) 构成新的类型。
🌰
typescript
interface Todo {
title: string;
description: string;
completed: boolean;
}
// 从Todo中挑选 'title' | 'completed'
type TodoPreview = Pick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false
}
// 💡 type TodoPreview = { title: string; completed: boolean; }TIP
typescript
// 从 T中挑选想要的属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}6️⃣ Omit<Type, Keys>
从 Type 中挑选要被移除的属性 Keys(字符串字面量或字面量联合)构成新的类型。
🌰
typescript
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
// 忽略 'description'
type TodoPreview = Omit<Todo, 'description'>
// {title: string; completed: boolean; createdAt: number;}
const todo: TodoPreview = {
title: "Clean room",
completed: false,
createdAt: 1615544252770,
}
// 忽略 'completed' 和 'createdAt'
type TodoInfo = Omit<Todo, 'completed' | 'createdAt'>
// {title: string; description: string;}
const todoInfo: TodoInfo = {
title: 'Pick up kids',
description: 'Kindergarten closes at 5pm'
}TIP
基本上可以看出 Omit 是 Pick 的反操作,一个忽略不想要的,一个挑选想要的😎
typescript
// 利用了 keyof T 操作符
// [P in xx] 语法
// T[P] 索引类型
type Omit<T, K extends string | number | symbol> = {
[P in Exclude<keyof T, K>]: T[P]
}7️⃣ ⭐ Exclude<UnionType, ExcludedMembers>
通过从联合类型 UnionType 中排除 ExcludedMembers 部分构建新的类型。
🌰
typescript
// 从联合类型 'a' | 'b' | 'c' 排除成员 'a'
// 因此 type T0 = 'b' | 'c'
type T0 = Exclude<'a' | 'b' | 'c', 'a'>
// 从联合类型 'a' | 'b' | 'c' 排除成员 'a' 和 'b'
// 因此 type T1 = 'c'
type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'>
// 从联合类型中 排除 函数类型成员🤩
// 因此 type T2 = string | number
type T2 = Exclude<string | number | (() => void), Function>TIP
typescript
type Exclude<T, U> = T extends U ? never : T;8️⃣ ⭐ Extract<Type, Union>
通过从 Type 中提取可分配给Union的所有联合成员来构造类型。
🌰
typescript
// 从类型 'a' | 'b' | 'c' 中提取可分配给联合类型 'a' | 'f' 中的成员
// 这里只有 `a` 满足类型类型😎
// 因此 type T0 = 'a'
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>
// 只有 () => void 满足,因此被提取出来
// type T1 = () => void
type T1 = Extract<string | number | (() => void), Function>TIP
typescript
// 即只将T继承了U的部分提取出来
type Extract<T, U> = T extends U ? T : never;9️⃣ NonNullable<Type>
从 Type 中排除 null & undefined 这些空类型来构造新的类型。
🌰
typescript
// 排除 undefined 类型
// 因此 type T0 = string | number
type T0 = NonNullable<string | number | undefined>
// type T1 = string[]
type T1 = NonNullable<string[] | null | undefined>TIP
typescript
type NonNullable<T> = T extends null | undefined ? never : T🔟 🤩 Parameters<Type>
从用作是函数的参数类型 Type 中构造一个元组(tuple)类型。
🌰
typescript
declare function f1(arg: {a: number, b: string}): void;
// 参数为空 因此 type T0 = []
type T0 = Parameters<() => string>;
// type T1 = [s: string] 元组类型
type T1 = Parameters<(s: string) => void>;
// 泛型🤩
// type T2 = [arg: unknown]
type T2 = Parameters<<T>(arg: T) => T>;
// typeof 操作符
// type T3 = [arg: {
// a: number;
// b: string;
// }]
type T3 = Parameters<typeof f1>;
// type T4 = unknown[] 🤩
type T4 = Parameters<any>;
// type T5 = never 🤩
type T5 = Parameters<never>;
// ❌ 类型 'string' 不能满足 '(...args: any) => any' 的约束
// 即这里的 string 类型不是一个函数
// type T6 = never
type T6 = Parameters<string>;
// ❌ 类型 'Function' 不能满足 '(...args: any) => any' 的约束
// 类型 'Function' 没有提供对 '(...args: any) => any' 签名的匹配
// type T7 = never
type T7 = Parameters<Function>TIP
typescript
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any
? P
: never1️⃣1️⃣ ConstructorParameters<Type>
从构造器函数的参数中构建一个元组或数组类型。它生成所有参数类型的元组类型(或当 Type 不是一个函数时,返回的类型为 never)。
🌰
typescript
// 元组
// type T0 = [message?: string]
type T0 = ConstructorParameters<ErrorConstructor>;
// 数组
// type T1 = string[]
type T1 = ConstructorParameters<FunctonConstructor>;
// 元组
// type T2 = [pattern: string | RegExp, flags?: string]
type T2 = ConstructorParameters<RegExpConstructor>;
// type T3 = unknown[]
type T3 = ConstructorParameter<any>;
// ❌ 类型 'Functon' 不能满足 'abstract new (...args: any) => any' 约束
// 类型 'Functon' 不能匹配 `new (...args: any): any`
// type T4 = never
type T4 = ConstructorParameters<Function>定义
typescript
type ConstructorParameters<T extends abstract new (...args: any) => any> =
T extends abstract new (args: infer P) => any
? P
: never1️⃣2️⃣ ⭐ ReturnType<Type>
将函数的返回类型 Type 构造一个类型。
🌰
typescript
declare function f1(): {a: number, b: string}
// 函数的返回类型是 string
// 因此 type T0 = string
type T0 = ReturnType<() => string>
// type T1 = void
type T1 = ReturnType<(s: string) => void>
// type T2 = unknown
type T2 = ReturnType<<T>() => T>;
// T 继承 U, U又继承number[]
// type T3 = number[]
type T3 = ReturnTYpe<<T extends U, U extends number[]>() => T>;
// 🤩 很常见的一种获取函数返回类型的方式
// typeof f1 获取函数的签名为 () => {a: number, b: string}
// type T4 = {a: number, b: string}
type T4 = ReturnType<typeof f1>;
// 🤔
// type T5 = any
type T5 = ReturnType<any>;
// 🤔
// type T6 = never
type T6 = ReturnType<never>;
// ❌ 类型 'string' 不能满足 '(...args: any) => any' 约束
type T7 = ReturnType<string>;
// ❌ 类型 'Function' 不能满足 '(...args: any) => any' 约束
// ❌ 类型 'Function' 不能匹配 '(...args: any): any' 签名
type T8 = ReturnType<Function>;定义
typescript
// 💡 使用 'infer' 进行类型推断
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any1️⃣3️⃣ InstanceType<Type>
构造一个由传入的 Type 构造函数的实例类型组成的类型。
🌰
typescript
class C {
x = 0;
y = 0;
}
// 💡类既可以作为值也可以作为类型使用 let c: C = new C()
// type T0 = C;
type T0 = InstanceType(typeof C);
// type T1 = any;
type T1 = InstanceType(any);
// type T2 = never;
type T2 = InstanceType<never>;
// ❌ 类型 'string' 不能满足 'abstract new (...args: any) => any' 约束
// 翻译一下,string不是一个类
// type T3 = any;
type T3 = InstanceType<string>;
// ❌ 类型 'Function' 不能满足 'abstract (...args: any) => any' 约束
// ❌ 类型 'Function' 不能匹配 'new (...args: any): any' 签名
// type T4 = any;
type T4 = InstanceType<Function>;TIP
typescript
// 这个和 ConstructorParameters 有点像
// ConstructorParameters 是推断构造函数参数
// InstanceType 推断的是构造函数的返回类型 即实例类型
type InstanceType<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: any) => infer R
? R
: any1️⃣4️⃣ ThisParameterType<Type>
提取函数类型的 this 参数的类型,如果函数类型不存在 this 参数,则返回 unknown。
🌰
typescript
function toHex(this: Number) {
return this.toString(16);
}
function numberToString(n: ThisParamterType<typeof toHex>) {
return toHex.apply(n);
}TIP
typescript
// 先判断 this 参数存不存在
type ThisParameterType<T> =
T extends (this: infer U, ...args: any) => any
? U
: unknown;1️⃣5️⃣ OmitThisParameterType<Type>
从 Type 中移除 this 参数。如果 Type 没有显式声明 this 参数,结果直接返回 Type。否则,一个从 Type 类型中排除了 this 参数的新类型会被创建。泛型会被擦除,只有最后一个重载签名会传播为新的函数类型。
🌰
typescript
function toHex(this: Number) {
return this.toString(16);
}
// type fiveToHex = () => string;
// this 参数被移除
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
console.log(fiveToHex());TIP
typescript
// 先判断 this 参数存不存在 unknown extends ThisParameterType<T>
// 不存在直接返回 类型T
// 如果存在,然后判断是不是一个函数,并对函数参数和返回值进行推断
// T extends (...args: infer A) => infer R
// 如果是函数返回函数签名 (...arg: A) => R
// 如果不是,则直接返回类型T
type OmitThisParameterType<T> =
unknown extends ThisParameterType<T>
? T
: T extends (...args: infer A) => infer R
? (...arg: A) => R
: T;1️⃣6️⃣ ThisType<Type>
这个工具不会返回转换后的类型,而是充当 this 类型的标记。🚨要使用这个工具,noImplictThis 编译选项必须开启。
🌰
typescript
type ObjectDescriptor<D, M> = {
data?: D;
// methods中的 'this' 类型是 D & M
methods?: M & ThisType<D & M>;
}
function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
let data: object = desc.data || {}
let methods: object = desc.methods || {}
return { ...data, ...methods } as D & M
}
let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx // Strongly typed this
this.y += dy // Strongly typed this
}
}
})
obj.x = 10
obj.y = 20
obj.moveBy(5, 5)在上面的例子中,makeObject的参数中的 methods 对象有一个上下文类型,包括ThisType<D & M>,因此方法对象中的 this 的类型是{x: number, y: number}&{moveBy(dx: number, dy: number): number}。注意methods属性的类型是如何同时作为方法中 this 类型的推断目标和源的。
ThisType<T> 标记类型是在 lib.d.ts 中的一个简单空接口(interface)声明。除了在对象字面量上下文类型中被识别外,该接口表现的就像一个空的接口。
1️⃣7️⃣ 内置字符串类型操作
Uppercase<StringType>Lowercase<StringType>Capitalize<StringType>Uncapitalize<StringType>
为了帮助对模板字符串字面量的操作,TypeScript包含了一组类型,可以在类型系统中用于进行字符串操作。可以在 Template Literal Types 文档找到更多。
原文档:
2022年09月11日00:24:30