Typescript 提供了一些工具类型来辅助进行常见的类型转换,非常方便,本文对这些工具类型的常见用法及原理做一个汇总。
Partial
用于构造一个type下面所有属性都设置为可选的类型,这个工具会返回一个给定类型的子集。
interface Todo {
title: string;
description: string;
}
type MyTodo = Partial<Todo>;
//结果
type MyTodo = {
title?: string | undefined;
description?: string | undefined;
}
如果要实现一个MyPartial,达到相同的功能,可以这么做
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
interface Todo {
title: string;
description: string;
}
type MyTodo = MyPartial<Todo>;
效果和上述一样
Required
用于构造一个Type下面的所有属性全都设置为必填的类型, 和Partial效果刚好相反
interface Props {
a?: number;
b?: string;
c: number;
}
type PropsType = Required<Props>;
//输出
type PropsType = {
a: number;
b: string;
c: number;
}
要实现一个MyRequired,可以这么做
type MyRequired<T> = {
[P in keyof T]-?: T[P];
};
-? 是一个类型修饰符,用于移除属性的可选性(即从可选属性变为必选属性)
Readonly
用于构造一个Type下面的所有属性全都设置为只读的类型,意味着这个类型的所有的属性全都不可以重新赋值。
interface Todo {
title: string;
description: string;
}
type todo1 = Readonly<Todo>;
//输出
type todo1 = {
readonly title: string;
readonly description: string;
}
如果要实现一个Myreadonly,可以这么做
type Myreadonly<T> = {
readonly [P in keyof T]: T[P]
}
Record
这个工具类新的API是这样:Record<keys,type>, 用于构造一个对象类型,它所有的key(键)都是Keys类型,它所有的value(值)都是Type类型。这个工具类型可以被用于映射一个类型的属性到另一个类型。
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
type cat1 = Record<CatName, CatInfo>;
//输出
type cat1 = {
miffy: CatInfo;
boris: CatInfo;
mordred: CatInfo;
};
现在有这样一个场景,后端返回的数据中数据不确定,该如何去定义数据类型呢,比如这样
interface CatInfo {
age: number;
breed: string;
}
let mimi: CatInfo = { age: 2, breed: "tabby" };
mimi.size = "20";
想对CatInfo去做一个扩展,怎么做比较好呢,可以通过索引签名来做
interface CatInfo {
age: number;
breed: string;
[kay: string]: string | number;
}
还可以通过和Record<string, any>取交叉类型
type CatInfo = {
age: number;
breed: string;
} & Record<string, any>
let mimi: CatInfo = { age: 2, breed: "tabby" };
mimi.size = "20";
如果要实现应一个Myrecord,可以这样做
type MyRecord<K extends keyof any, T> = {
[P in K]: T
}
pick
他的api是这样的pick<Type,keys>, 用于构造一个类型,它是从Type类型里面挑了一些属性Keys(Keys是字符串字面量 或者 字符串字面量的联合类型)
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
要实现这个,做过type-challenges对这个应该非常熟悉,就是第一个题
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
Exclude
他的api是这样Exclude<UnionType,ExcludedMembers>,用于构造一个类型,它是从UnionType联合类型里面排除了所有可以赋给ExcludedMembers的类型。
type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"
要实现的话,可以这么做
type MyExclude<T, U> = T extends U ? never : T
Omit
用于构造一个类型,它是从Type类型里面过滤了一些属性Keys(Keys是字符串字面量 或者 字符串字面量的联合类型)
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
type TodoPreview = Omit<Todo, "description">;
//输出
type TodoPreview = {
title: string;
completed: boolean;
createdAt: number;
}
要实现的话,可以这么做
type MyOmit<T, K> = MyPick<T, MyExclude<keyof T, K>>
Extract
api是这样Extract<Type,Union> 用于构造一个类型,它是从Type类型里面提取了所有可以赋给Union的类型。
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"
要实现的话可以这么做
type MyExtract<T, U> = T extends U ? T : never;
NonNullable
用于构造一个类型,这个类型从Type中排除了所有的null、undefined的类型
type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
要实现的话,可以这么做
type MyNonNullable<T> = T extends null | undefined ? never : T;
Parameters
用于根据所有Type中函数类型的参数构造一个元祖类型。
type T0 = Parameters<() => string>;
// type T0 = []
type T1 = Parameters<(s: string) => void>;
// type T1 = [s: string]
type T2 = Parameters<<T>(arg: T) => T>;
// type T2 = [arg: unknown]
要实现的话可以这么做
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
ConstructorParmeters
用于根据Type构造函数类型来构造一个元祖或数组类型,它产生一个带着所有参数类型的元组(或者返回never如果Type不是一个函数)。
type T0 = ConstructorParameters<ErrorConstructor>;
// type T0 = [message?: string]
type T1 = ConstructorParameters<FunctionConstructor>;
// type T1 = string[]
type T2 = ConstructorParameters<RegExpConstructor>;
// type T2 = [pattern: string | RegExp, flags?: string]
type T3 = ConstructorParameters<any>;
// type T3 = unknown[]
可以这么来实现这个功能
type MyConstructorParameters<T extends abstract new (...args: any[]) => any> =
T extends abstract new (...args: infer P) => any ? P : never;
ReturnType
用于构造一个含有Type函数的返回值的类型。
type T0 = ReturnType<() => string>;
// type T0 = string
type T1 = ReturnType<(s: string) => void>;
// type T1 = void
type T2 = ReturnType<<T>() => T>;
// type T2 = unknown
可以这么来实现这个功能
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;
InstanceType
用于构造一个由所有Type的构造函数的实例类型组成的类型。
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>;
// type T0 = C
type T1 = InstanceType<any>;
// type T1 = any
type T2 = InstanceType<never>;
// type T2 = never
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
type PersonInstance = InstanceType<typeof Person>; // Person
手动实现的话,这么做
type MyInstanceType<T extends abstract new (...args: any[]) => any> =
T extends abstract new (...args: any[]) => infer R ? R : never;