0%

ts类型体操1

对应任何类型T,keyof T的结果为该类型上索引公有属性key的联合

1
2
3
4
5
6
7
8
9
10
11
12
interface Eg1 {
name: string,
readonly age: number
}
type T1=keyof Eg1//name|age
class Eg2{
private name: string,
public readonly age:number,
protected home:string
}
//T2被约束为age,非公有属性不能被keyof获取
type T2=keyof Eg2

T[k]索引查询:

1
2
3
4
5
6
7
8
interface Eg1 {
name:string,
readonly age: number,
}
type V1=Eg1['name']//string
type V2=Eg1['name'|'age']//string | number
type V3=Eg1['name'|'age222']//any
type V4=Eg1[keyof Eg1]//strign | number

[]中的key有不存在于T中的,则是any

第一个元素类型:

1
type First<T extends any[]> = T extends [infer F,...infer R] ? F : nerver

获取元组长度

1
type Length<T extends readonly any[]> = T<'length'>

元组转数组

注意加上readonly,因为as const 会生成如下类型:const tuple: readonly[‘tesla’,’model 3’,’model X’,’model Y’]

1
2
3
4
5
6
const tuple = ['tesla','model 3','model X','model Y'] as const
const res: TupleToObject<typeof tuple>
// expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
type TupleToObject<T extends readonly any[]> = {
[K in T[number]]: K
}

Ts内置类型工具原理分析:

实现Partial:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*核心实现就是通过映射类型遍历T上的所有属性
然后将每个属性设置为可选属性 */
type Partial<T> = {
[P in keyof T]?: T[P]
}
/*将制定的key变为可选类型*/
type PartialOptional<T,K extends keyof T> = {
[P in K]?: T[P]
}
/*example:type Eg1={key1?:string,key2?:number}*/
type Eg1 = PartialOptional<{
key1: string,
key2:number,
key3:''
},'key1'|'key2'>

实现Record:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//key 为联合类型中的每个子类型,类型为T
//keyof any得到的是string | number |symbol
//类型key的类型只能为string | number | symbol
type Record<K extends keyof any, T>={
//遍历key,将值设为T
[P in K]:T
}
/*example: type Eg2 = {a:B,b:B}*/
interface A {
a:string,
b:number
}
interface B {
key1: number,
key2: string
}
type Eg2=Record<keyof A,B>
/*Partial,Readonly,Pick同态,其实现需要输入类型T拷贝属性,因此属性修饰符例如readonly,?:会被拷贝*/
type Eg=Pick<{readonly a?: string},'a'>
//keyof T 辅助拷贝传入类型的属性
//type Eg=<{readonly a?: string},'a'>
type Pick<T,K extends keyof T>={
[P in K]:T[P]
}
/*在Pick中,P in keyof T,T为输入的类型,而keyof T则遍历了输入类型,而Record的实现中,并没有遍历输入的类型,K只是约束为keyof any的子类型*/

实现Readonly

1
2
3
4
5
6
7
8
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
}
//example
type Eg=MyReadonly<{
key1: string,
key2: number
}>

实现Pick:

1
2
3
4
5
6
7
8
9
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
interface Todo{
title: string
description: string
completed: boolean
}
type A=MyPick<Todo,'title'| 'completed'>

Exclude:

1
2
3
4
5
/*遍历T中的所有子类型,如果该子类型约束于U(存在于U,兼容于U),则返回nerver类型,否则返回该子类型 */
type Exclude<T,U> = T extends U ? nerver : T
//example: type Eg = 'key1'
type Eg=Exclude<'key1'|'key2','key2'>
//nerver表示一个不存在的类型,与其他类型联合后,是没有nerver的

Extract:

1
2
//提取联合类型T和U的交集
type Extract<T,U> = T extends U ? T : never

Omit:

1
2
3
4
5
6
7
//Omit<T,K>从类型T中剔除K中的所有属性
//用Pick实现
type Omit = Pick<T,Exclude<keyof T,K>>
//利用映射类型
type Omit2<T,K extends keyof any> = {
[P in Exclude<keyof T,K>]:T[P]
}

Parameters和ReturnType

1
2
3
4
5
6
7
8
9
//Parameters获取函数参数类型,将每个参数类型放在一个元组
type Parameters<T extends Function> = T extends (...args: infer P)=>any ? P : never;
//example
type Eg = Parameters<(arg1:string,arg2:number) => void>
//type Eg=[arg1:string,arg2:number]
/*Parameters约束参数T必须是个函数类型,具体实现就是判断T是否是函数类型,是就是要infer P让ts自己推到出函数的参数类型,并将推导结果存到类型P,否则
返回never*/
//ReturnType获取函数的返回值类型
type ReturnType<T extends Function> = T extends (...args: any)=> infer R ? R : never

ConstructorParameters:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//ConstructorParameters可以获取类的构造函数的参数类型,存在一个元组中
//用infer进行推导
type ConstructorParameters<T extends abstract new (...args:any)=>any>=T extends abstract new (...args:infer P) =>any ? P : never
/**
* @example
* type Eg = string;
*/
interface ErrorConstructor {
new(message?: string): Error;
(message?: string): Error;
readonly prototype: Error;
}
type Eg = ConstructorParameters<ErrorConstructor>;

/**
* @example
* type Eg2 = [name: string, sex?: number];
*/
class People {
constructor(public name: string, sex?: number) {}
}
type Eg2 = ConstructorParameters<typeof People>
//T约束为抽象类:既可以赋值为抽象类,也可以赋值为普通类
/*使用typeof 类作为类型和使用类作为类型的区别:
当把类直接作为类型,该类型约束的必须是类的实例,即该类型获取的是该类上的实例属性和实例方法
当把typeof 类作为类型,约束的满足该类的类型,该类型获取的是该类上的静态属性和方法
*/
//example
class People {
name: string;
age: number;
constructor()
}
//p1可以正常赋值
const p1: People = new People()
//等号后的People报错
const p2: People =People
//p3报错,类型People中缺少属性'prototype',但类型"typeof People中需要该属性
const p3: typeof People = new People()
const p4: typeof People = People

自定义的Ts高级类型工具:

SymmetricDifference:

1
2
3
4
//SymmetricDifference<T,U>获取没有同时存在于T和U内的类型
type SymmetricDifference<T,U>=Exclude<T|U,T&U>
//example:type Eg ='1' | '4'
type Eg = SymmetricDifference<'1'|'2'|'3','2'|'3'|'4'>

FunctionKeys:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取T中所有类型为函数的key组成的联合类型
type NonUndefined<T>= T extends undefined ? nerver : T
type FunctionKeys<T extends object> = {
[K in keyof T]: NonUndefined<T[K]> extends FunctionKeys ? K : nerver
}[keyof T]
/**
* @example
* type Eg = 'key2' | 'key3';
*/
type AType = {
key1: string,
key2: () => void,
key3: Function,
};
type Eg = FunctionKeys<AType>;
//最后经过{省略}[keyof T]索引访问,取到的为值类型的联合类型never | key2 | key3,计算后就是key2 | key3;
/*T[]是索引访问操作,可以取到值的类型,T['a'|'b']可以依次取到值的类型进行联合,
T[keyof T]则是取到T所有值的类型类型 nerver和其他类型进行联合时,nerver不存在,nerver | number | string = number | string */

OptionalKeys:

1
2
3
4
5
6
7
8
9
10
11
//OptionalKeys提取T中所有可选类型的key组成的联合类型
/*用映射类型遍历所有key,通过Pick<T,P>提取当前key和类型,利用同态拷贝会拷贝可选修饰符的特性,利用{}extends {当前key:类型} 判断是否是可选类型*/
type OptionalKeys<T> = {
[P in keyof T]: {} extends Pick<T,P> ? P : nerver
}[keyof T]

type Eg = OptionalKeys<{key1?: string, key2: number}>
//{key1?:string}
//利用{}extends {当前key:类型} 判断是否是可选类型,{}和只包含可选参数类型{key1?:string}是兼容这一特性,extends前面的{}替换为object也可以
type Eg2={} extends {key1:string} ? true : false //false
type Eg3={} extends {key1?:string} ? true : false //true

增强Pick:

PickByValue

1
2
3
4
5
6
7
8
9
10
11
12
13
//PickByValue提取指定类型的键值对
//获取T中类型不为nerver的类型组成大的联合类型
type TypeKeys<T> = T[keyof T]
type PickByValue<T,V> = Pick<T,
TypeKeys<{[P in keyof T]: T[P] extends V ? P : nerver}>
>
/*example
type Eg = {
key1: number;
key3: number
}*/
type Eg = PickByValue<{key1:number,key2:string,key3:number},number>

PickByValueExtract:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Ts类型兼容的原因,string可以分配给string|number
type PickByValueExact<T,V>=Pick<T,
TypeKeys<{[P in keyof T]: [T[P]] extends [V]}
? ([V] extends [T[P]] ? P : never)
: never
>
>
//type Eg={b:number}
type Eg1=PickByValueExact<{a:string,b:number},number>
type Eg2=PickByValueExact<{a:string,b:number,c:number|undefined},number>
//type Eg2={b:number,c:number|undefined}
/*给泛型套一层元组,规避extends的分发式联合类型的特性
利用两个类型互相兼容的方式判断是否相同*/
//example
type Eq1<X,Y> = X extends Y ? true : false
type Eq2<X,Y> = [X] extends [Y] ? true : false
type Eq3<X,Y>= [X] extends [Y]
? ([Y] extends [X] ? true : false)
: false

增强Omit:

1
2
3
4
5
6
7
//Omit<T,K>从类型T中剔除K中的所有属性
//用Pick实现
type Omit = Pick<T,Exclude<keyof T,K>>
//利用映射类型
type Omit2<T,K extends keyof any> = {
[P in Exclude<keyof T,K>]:T[P]
}

OmitByValue:

1
2
3
4
5
6
7
8
9
10
type TypeKeys<T> = T<keyof T>
type OmitByValue<T,V> = Omit<T,
TypeKeys<{[P in keyof T] : T[P] extends V ? P : never}>
>

type OmitByValueExact<T,V> =Omit<T,
TypeKeys<{[P in keyof T]: [T[P]] extends [V]
?([V] extends [T[P]] ? [T[P]] : never)
: never
}>

Overwrite和Assign

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//从T中提取存在于U中的key和对应的类型
type Intersection<T extends object,U extends object>=Pick<T,
Extract<keyof T,keyof U> & Extract<keyof U,keyof T>
>
type Eg=Intersection<{key1: string},{key1: string,key2:number}>
/*利用Pick提取指定的key组成的类型,通过Extract<keyof T,keyof U>提取同时存在于T和U中的key,
Extract<keyof U,keyof T>同理,做两次Extract然后再交叉类型的原因在于处理类型的兼容推导问题*/
//Diff<T,U>从T中排除存在于U中的key和类型
type Diff<T extends object,U extends object> = Pick<
T,Exclude<keyof T,keyof U>
>
type Overwrite<
T extends object,
U extends object,
I = Diff<T,U> & Intersection<T,U>
> = Pick<I,keyof I>
/*Diff<T,U>先获取存在于T不存在于U中的key和其类型,
Intersection<U,T>从U中提取存在于T中的key和类型,即后者同名key和类型,在进行交叉合并*/

type Assign<
T extends object,
U extends object,
I = Diff<T,U> &U
> = Pick<IDBCursor,keyof I>

Intersection:

1
2
3
4
5
6
7
8
9
10
11
//从T中提取存在于U中的key和对应的类型
type Intersection<T extends object,U extends object>=Pick<T,
Extract<keyof T,keyof U> & Extract<keyof U,keyof T>
>
type Eg=Intersection<{key1: string},{key1: string,key2:number}>
/*利用Pick提取指定的key组成的类型,通过Extract<keyof T,keyof U>提取同时存在于T和U中的key,
Extract<keyof U,keyof T>同理,做两次Extract然后再交叉类型的原因在于处理类型的兼容推导问题*/
//Diff<T,U>从T中排除存在于U中的key和类型
type Diff<T extends object,U extends object> = Pick<
T,Exclude<keyof T,keyof U>
>

UnionToIntersection:

1
2
3
4
5
6
7
8
9
10
//将联合类型转变为交叉类型
type UnionToIntersection<T> =(T extends any
? (arg: T) => void
: never
)extends (arg: infer U) => void ? U : never
/*T extends any ? (arg: T) => void : never一定走true分支,构造一个逆变的联合类型
(arg1: T1)=>void | (arg2: T2)=>void | (arg3: T3)=>void
再利用第二个extends配合infer推导得到U的类型,利用infer对协变类型的特性得到交叉类型*/
type Eg = UnionToIntersection<{key1:string} |{key2: number}>