问题引出
我们需要渲染一个表格,往往需要定义:
interface Row {
user: string
email: string
id: number
vip: boolean
// ...
}
const tableDatas: Row[] = []
有时候我们也需要表格对应的搜索表单,需要其中一两个搜索项,如果刚接触 typescript
的同学可能会立刻这样写:
interface SearchModel {
user?: string
id?: number
}
const model: SearchModel = {
user: '',
id: undefined
}
这样写会出现一个问题,如果后面 id
类型要改成 string
,我们需要改 2 处地方,不小心的话可能就会忘了改另外一处。所以,有些人会这样写:
interface SearchModel {
user?: Row['user']
id?: Row['id']
}
这固然是一个解决方法,但事实上,我们前面已经定义了 Row
类型,这其实是可以更优雅地复用的:
// Partial 和 Pick 为内置类型
const model: Partial<Row> = {
user: '',
id: undefined
}
// 或者需要明确指定 key 的,可以
const model2: Partial<Pick<Row, 'user'|'id'>>
上面使用到的 Partial
和 Pick
都是 typescript
内置的类型别名。
内置类型
Partial<T>
将类型 T 的所有属性标记为可选属性
type Partial<T> = {
[P in keyof T]?: T[P];
};
使用场景:
// 账号属性
interface AccountInfo {
name: string
email: string
age: number
vip: 0|1 // 1 是vip ,0 是非vip
}
// 当我们需要渲染一个账号表格时,我们需要定义
const accountList: AccountInfo[] = []
// 但当我们需要查询过滤账号信息,需要通过表单,
// 但明显我们可能并不一定需要用到所有属性进行搜索,此时可以定义
const model: Partial<AccountInfo> = {
name: '',
vip: undefind
}
Required<T>
与 Partial
相反,Required
将类型 T
的所有属性标记为必选属性
type Required<T> = {
[P in keyof T]-?: T[P];
};
Readonly<T>
将所有属性标记为 readonly
, 即不能修改
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Pick<T, K>
从 T 中过滤出属性 K
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
使用场景:
interface AccountInfo {
name: string
email: string
age: number
vip?: 0|1 // 1 是vip ,0 是非vip
}
type CoreInfo = Pick<AccountInfo, 'name' | 'email'>
/*
{
name: string
email: string
}
*/
Record<K, T>
同时标记对象的 key
和 value
的类型
type Record<K extends keyof any, T> = {
[P in K]: T;
};
使用场景:
// 定义 学号(key)-账号信息(value) 的对象
const accountMap: Record<number, AccountInfo> = {
10001: {
name: 'xx',
email: 'xxxxx',
// ...
}
}
const user: Record<'name'|'email', string> = {
name: '',
email: ''
}
// 复杂点的类型推断
function mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>
const names = { foo: "hello", bar: "world", baz: "bye" };
// 此处推断 K, T 值为 string , U 为 number
const lengths = mapObject(names, s => s.length); // { foo: number, bar: number, baz: number }
Exclude<T, U>,Omit<T, K>
移除 T 中的 U 属性
type Exclude<T, U> = T extends U ? never : T;
使用场景:
// 'a' | 'd'
type A = Exclude<'a'|'b'|'c'|'d' ,'b'|'c'|'e' >
乍一看好像这个没啥卵用,但是,我们通过一番操作,之后就可以得到 Pick
的反操作:
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type NonCoreInfo = Omit<AccountInfo, 'name' | 'email'>
/*
{
age: number
vip: 0|1,
}
*/
Extract<T, U>
Exclude
的反操作,取 T,U
两者的交集属性
type Extract<T, U> = T extends U ? T : never;
使用 demo:
// 'b'|'c'
type A = Extract<'a'|'b'|'c'|'d' ,'b'|'c'|'e' >
这个看起来没啥用,实际上还真没啥卵用,应该是我才疏学浅,还没发掘到其用途。
NonNullable<T>
排除类型 T
的 null | undefined
属性
type NonNullable<T> = T extends null | undefined ? never : T;
使用 demo
type A = string | number | undefined
type B = NonNullable<A> // string | number
function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
let s1: string = x; // Error, x 可能为 undefined
let s2: string = y; // Ok
}
Parameters<T>
获取一个函数的所有参数类型
// 此处使用 infer P 将参数定为待推断类型
// T 符合函数特征时,返回参数类型,否则返回 never
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
使用demo
:
interface IFunc {
(person: IPerson, count: number): boolean
}
type P = Parameters<IFunc> // [IPerson, number]
const person01: P[0] = {
// ...
}
另一种使用场景是,快速获取未知函数的参数类型
import { somefun } from 'somelib'
// 从其他库导入的一个函数,获取其参数类型
type SomeFuncParams = Parameters<typeof somefun>
// 内置函数
// [any, number?, number?]
type FillParams = Parameters<typeof Array.prototype.fill>
ConstructorParameters<T>
类似于 Parameters<T>
, ConstructorParameters
获取一个类的构造函数参数
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
使用 demo:
// string | number | Date
type DateConstrParams = ConstructorParameters<typeof Date>
ReturnType<T>
获取函数类型 T
的返回类型
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
使用方式和 Parameters<T>
类似,不再赘述
InstanceType<T>
获取一个类的返回类型
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
使用方式和 ConstructorParameters<T>
类似,不再赘述
网友评论