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;
 同理,也很好理解,只是从函数返回值类型换成了类的实例类型。
未完待续哦...