针对基础的Ts知识不做过多解释,相关文档查阅即可.本文注重对于类型操作中的难点和日常项目中的高频类型等做整理.本文会在内置的高级类型基础上延展.
针对内置的高级类型,这里按照操作类型做了简单的分类.
1.函数类
1.Parmeters<T>
/** |
2.ReturnType<T>
/** |
以上两个高级类型中有三个知识点
1. 类型约束 extends
入参部分的T extends (...args: any) => any
用来约束入参的类型为函数类型
2. 条件:extends ? :
类似JS中的三元运算,比如T extends number?true:false
经过计算返回布尔类型,如果T的类型是number则返回true,否则false
3.infer 推导
当在类型计算过程中需要提取其中的类型时,我们可以再适当的位置加 infer
来声明部分类型
2.元组类
1.Exclude<T,U>
/** |
2.Extract<T,U>
/** |
这其中有个重要的知识点就是分布式条件类型,即类型参数为联合类型时,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型.
分布式条件类型条件:
1. 入参为联合类型
2. extends
左边直接对联合类型进行引用 A extends A 是分布式条件类型 ;[A] extends [A] 不是是分布式条件类型
所以再回头分析Exclude<'a' | 'b' | 'c' | 'd', 'a' | 'b'>
,T extends U
右边类型保持不变,将'a' | 'b' | 'c' | 'd'
拆开依次传入,第一次结果'a' extends 'a'|'b'
,结果为never;第二次'b' extends 'a'|'b'
,结果为never;第三次'c' extends 'a'|'b'
,结果为'c'
;第四次'd' extends 'a'|'b'
,结果为'd'
,最后将结果在重新组合成联合类型即'c'|'d'
;
3.索引类型
1.Partial<T>
/** |
2.Required<T>
/** |
3.Readonly<T>
/** |
以上三个类型都用到一个知识点是索引类型的重新构造,索引类型有readonly(只读),?(可选)两种修饰符,这个过程中可以对Value值
和Key值
进行修改,Value值修改直接写入新的类型即可,Key值的修改要用的as,即重映射
下面的例子将Value值类型全部改写为boolean
type Mapping<Obj extends object> = { |
下面的例子是将索引类型的Key大写
因为索引可能是string、number、symbol,而Uppercase只能接受string所以通过交叉类型来限制入参
type UppercaseKey<Obj extends object> = { |
上面三种内置的高级类型具有代表性,其余的比如构造函数和Promise相关的类型可以参考官网文档,因为不具有代表性,不做过得解释.额外添加了几个自定义高级类型,来总结其余的知识点
1.递归循环与模板字面量
CamelCaes<T>
,将连字符字符串转驼峰,比如start_time=>startTime,
type CamelCaes<S extends string> = S extends `${infer First}_${infer Rest}` |
上面的自定义类型用到了两个知识点:模板字面量
和递归循环
模板字面量,类似JS中的语法一样,在合适的位置通过infer声明变量,再通过条件类型提取出来.
下面的例子,我们通过_下划线来构建,左边部分和右边部分,下划线左边部分保持不变,下划线右边部分调用内置类型Capitalize
将首字母大写;但是仅仅这样,如果超过三个以上的单词则无法满足; type CamelCaes<S extends string> = S extends `${infer First}_${infer Rest}`
? `${First}${Capitalize<Rest>}`
: S;
type Res = CamelCaes<'activity_product_detail'>;
//expected to be 'activityProduct_detail'
所以对于右边的剩余部分,我们就要采用递归的方法,继续调用;${First}${CamelCaes<Capitalize<Rest>>}
2.模板字面量与联合类型
字符串类型中遇到联合类型的时候,会每个元素单独传入计算,无需再进行递归循环,最典型的例子就是BEM规范type BEM<
B extends string,
E extends string[],
M extends string[]
> = `${B}__${E[number]}--${M[number]}`;
type res = BEM<'btn', ['price'], ['success', 'error']>;
//expected to be "btn__price--success" | "btn__price--error"
3.any类型的交叉类型
any 类型与任何类型的交叉都是 any,所以可以用这个特性来判断一个类型是否是anytype IsAny<T> = number extends (number & T) ? true : false
4.any类型作为参数
any 在条件类型中也比较特殊,如果类型参数为 any,会直接返回 trueType 和 falseType 的合并
type TestAny<T> = T extends number ? 1 : 2; |
5.元祖类型的length与数组length
元组类型的 length 是数字字面量,而数组的 length 是 number。type len=[1,2,3]['length]
//expected to be 3
type len2=number[]['length]
//expected to number
6.可选索引的索引可能没有,Pick 出来的就可能是 {}
type res = {} extends Pick<{ a?: 1 }, 'a'> ? true : false; |
通过这个特点可以自定义一个类型用来过滤可选索引type GetOptional<Obj extends Record<string, any>> = {
[
Key in keyof Obj
as {} extends Pick<Obj, Key> ? Key : never
] : Obj[Key];
}
反过来也过滤非可选索引
7.数值计算
这一部分在TS中算是比较难得知识点,核心用法就是利用数组的长度属性来出来.type res=[1,2,3]['length'] //expected to 3
type res=['a','b','c']['length'] //expected to 3
举例求两个数的和type BuildArray<
Length extends number,
Ele = unknown,
Arr extends unknown[] = []
> = Arr['length'] extends Length
? Arr
: BuildArray<Length, Ele, [...Arr, Ele]>;
type Add<Num1 extends number, Num2 extends number> = [
...BuildArray<Num1>,
...BuildArray<Num2>
]['length'];
//e.g.
type res = Add<4, 5>;expected to 9
其余的不过多解释,需要了解的请自行搜索
思考:过程中发现了这样一个问题,目前还在查阅资料;没想到合理的解释