ts内置类型实现
# Pick
顾名思义,该内置类型主要用于挑选需要的属性,剔除不必要的元素。
type myPick<T, U extends keyof T> = {
[P in U]: T[P];
}
interface obj {
a: number;
b: string;
}
const pickTest: myPick<obj, "a"> = {
a: 13123
};
2
3
4
5
6
7
8
9
10
11
12
这里的
myPick<obj, "a">
相当于type pickTest = { a: number }
从代码量上来看,实现一个Pick
内置类型还是比较轻松的,我们只要利用in
的特性进行条件限制,做到除需要被保留的元素外,都进行过滤掉即可。同时,通过extents keyof
来约束传入的类型必须是被挑选类型的子项,这样一个完整的Pick
类型就实现完成了。
# Exclude
用于剔除指定类型,局限在于它只能用于联合类型的剔除。
type myExclude<T, U extends keyof any> = T extends U ? never : T;
// 相当于:type excludeTest = string | boolean;
const excludeTest: myExclude<number | string | boolean, number>;
2
3
4
never
类型表示永远不会出现的值的类型。
ts
中的never
关键字在这里用就十分恰当了,一般情况应该用到它的场景应该很少。
整体而言也是属于比较简单的一种内置类型了,主要涉及到extends
与?:
表达式的配合,可以理解为T
中的类型如果是U
的子集,那么就返回never
表示删除。
# Extract
与Exclude
,正好相反,这里是取两个类型的交集。
type Extract<T, U> = T extends U ? T : never;
2
3
这里借助了我们熟悉的三元运算符?:
,这样就能判断哪些是属于T
和U
共同拥有的类型了。
使用场景:
type E = Extract<number | string | 'b', string | 'b' | 'd'> // 等价于 type E = string | 'b'
2
3
# Omit
这个内置类型就是用来互补Exclude
的,该泛型的主要用处就在于移除目标接口的指定属性。
type myPick<T, U extends keyof T> = {
[P in U]: T[P];
}
type myExclude<T, U extends keyof any> = T extends U ? never : T;
type myOmit<T, U extends keyof T> = myPick<T, myExclude<keyof T, U>>
interface obj {
a: number;
b: string;
}
// 等价于:type omitTest = { b: string };
const omitTest: myOmit<obj, 'a'> = {
b: '3123123'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这里我们可以清晰的看到Omit
结合了我们上述介绍的两个泛型,从而达到移除指定项的能力。
整体的含义还是比较好理解的,首先使用myExclude
泛型返回除需要被删除属性外的剩余属性的联合类型,然后借用myPick
选择出目标对象中需要保留的属性,最终返回一个新的删除操作完成的接口。
# Partial
这个泛型就比较简单了,主要用于将传入的接口属性变为可选。
type myPartial<T> = {
[P in keyof T]?: T[P]
}
interface obj {
a: number;
b: string;
}
// 等价于:type partialTest = { a?: number; b?: string}
const partialTest: myPartial<obj> = {}
2
3
4
5
6
7
8
9
10
11
使用了该泛型处理后的接口,则接口所有属性将变为可选的。
# Required
Required
泛型就刚好与Partial
相反,它是将传入的接口属性变为必传。
type myRequired<T> = {
[P in keyof T]-?: T[P]
}
interface obj {
a: number;
b: string;
}
// 等价于:type requiredTest = { a?: number; b?: string}
const requiredTest: myRequired<obj> = {
a: 13,
b: '323'
};
2
3
4
5
6
7
8
9
10
11
12
13
14
# Readonly
Readonly
也算是一种我们熟知的类型之一了,将指定类型变为只读。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
2
3
4
从类型定义来上看还是比较好理解的,给传入类型的所有属性值设定readonly
关键字,实现效果。
# Record
该类型主要用于将目标对象中的属性类型进行修改,并返回一个新的类型,具体实现如下。
type Record<K extends of keyof any, T> = {
[P in K]: T
}
2
3
如果上面其他类型都能看懂的话,这里的代码其实已经不需要太多赘述了,就类似于修改传入的类型对象上的属性类型。
主要应用场景如下:
interface Foo {
name: string;
age: number;
}
const fooOther: Record<Foo, any> = {
name: 999,
age: '243'
};
2
3
4
5
6
7
8
9
经过这番操作后,其实fooOther
的类型已经变成了如下情况:
interface FooOther {
name: any;
age: any;
}
2
3
4
# NonNullable
用于排除目标类型中null
或undefined
属性。
type NonNullable<T> = T extends null | undefined ? never : T;
示例:
const typeTest: NonNullable<string | undefined | null>;
2
3
这里typeTest
的类型就等价于:typeTest: string
;
# Parameters
获取函数的参数类型。
这里将会涉及到一个比较有意思的玩意:infer
,表示在 extends 条件语句中待推断的类型变量。
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
2
3
在T extends (...args: infer P) => any
中,infer P
表示待推断的函数参数类型,结合三元运算符,整体含义为,如果T
能赋值给(...args: infer P) => any
,则返回结果为(...args: infer P) => any
中的P
,否则返回never
示例:
type P = Parameters<(name: string, age: number) => any>; // [string, number]
2
3
# ConstructorParameters
获取构造函数的参数类型。这个与上面的原理差不多,都是借助infer
就能达到效果
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
2
3
示例
class TestClass {
constructor(public name: string, public age: number) {}
}
type Params = ConstructorParameters<typeof TestClass>; // [string, number]
2
3
4
5
6
7
# ReturnType
获取函数返回值的类型。
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
2
3
基本和上述两种没啥差别,这里应该比较好理解了,就是位置不一致,其他都差不多的。
# InstanceType
获取类的实例类型。
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
同理,也很好理解,只是从函数返回值类型换成了类的实例类型。
未完待续哦...