BFE是一个前端面试及学习网站,这篇文章将带你学习并理解BFE-TS
的1-20
题,进行TS
能力进阶。前端
须要必定的TS
基础git
学习并理解这些题目可让你的TS
能力飙升。github
1-15
题都是TS
的内置Utility types
,有用但简单,不过多描述。面试
映射类型添加 ? 标识typescript
type Partial<T> = {
[P in keyof T]?: T[P];
};
复制代码
映射类型删除 ? 标识数组
type Required<T> = {
[P in keyof T]-?: T[P];
};
复制代码
映射类型添加 readonly 标识markdown
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
复制代码
通常用于快速创建简单对象类型,K 是建立对象类型的索引类型,T 则是建立对象类型的值类型frontend
type Record<K extends keyof any, T> = {
[P in K]: T;
};
复制代码
选择部分 T 中的索引类型后将其映射成新的对象类型分布式
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
复制代码
选择部分 T 中的索引类型后将其从原来的 keyof T 中剔除映射成新的对象类型函数
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
复制代码
经过分布式条件类型(Distributive Conditional Types) 进行筛选,将知足条件的返回 never,由于联合类型忽略 never 从而达到剔除的做用
type Exclude<T, U> = T extends U ? never : T;
复制代码
同上,返回相反,只将知足条件的返回,不知足则被剔除
type Extract<T, U> = T extends U ? T : never;
复制代码
剔除 null | undefined 类型,代表非空
type NonNullable<T> = T extends null | undefined ? never : T;
复制代码
经过 infer 推断函数参数并返回
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
复制代码
经过 infer 推断 new 构造函数参数并返回, TS 中经过 new (args) => any 声明这个函数被 new 调用时所需的参数和返回值
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
复制代码
经过 infer 推断函数返回值
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
复制代码
经过 infer 推断 new 后返回的类型
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
复制代码
经过 infer 推断函数的 this 类型
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;
复制代码
因推断时自动忽略函数的 this 类型,所以直接经过推断后的参数类型和返回值类型包装成个函数返回便可
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T;
复制代码
上面为 TS 源码,但实际经过 BFE 测试还能够这么写,Parameters 本质仍是推断,所以会忽略 this:
type MyOmitThisParameter<T> = T extends (...args: any) => any ? ( ...args: Parameters<T> ) => ReturnType<T> : T
复制代码
实现FirstChar<T>
类型:
type A = FirstChar<'BFE'> // 'B'
type B = FirstChar<'dev'> // 'd'
type C = FirstChar<''> // never
复制代码
目前TS
支持字符串使用infer
推断,语法和模板字符串类似,所以使用infer
便可完成。
type FirstChar<T extends string> = T extends `${infer T}${any}` ? T : never
复制代码
实现LastChar<T>
类型:
type A = LastChar<'BFE'> // 'E'
type B = LastChar<'dev'> // 'v'
type C = LastChar<''> // never
复制代码
一样使用infer
推断字符串中的类型,模板字符串中的推断必须保证前面的${any}
匹配一个,身下的都丢给最后的${any}
匹配。
举个栗子:
用${infer A}${infer B}${infer C}
匹配'ab'
,A
在前面匹配了'a'
,B
也在前面所以匹配一个'b'
,剩下的都丢给C
,由于没有剩余的了,因此C
匹配到了''
。
晓得如何匹配了,那么再加上递归便可作出来
type LastChar<T extends string> = T extends `${infer F}${infer R}`
? R extends ''
? F
: LastChar<R>
: never
复制代码
过程:
'BFE' =>
F -> 'B', R -> 'FE'
R isn't ''
LastChar<'FE'>
F -> 'F', R -> 'E'
R isn't ''
LastChar<'E'>
F -> 'E', R -> ''
R is ''
F -> 'E'
复制代码
即经过一直拿出当前字符串类型的第一位而后判断剩下的是否为''
是的话就是最后一个了,不是就继续递归剩下的。
type Foo = [string, number, boolean]
type Bar = TupleToUnion<Foo> // string | number | boolean
复制代码
这题考察的是索引访问类型Indexed Access Types
,咱们可使用索引访问类型来查找另外一种类型上的特定属性,keyof
能够得到某类型的全部可访问的索引类型。
type TupleToUnion<T extends any[]> = T[number]
复制代码
数组类型有个特殊的索引类型number
,能够经过arr[number]
获取一个数组中元素的类型。
type A = FirstItem<[string, number, boolean]> // string
type B = FirstItem<['B', 'F', 'E']> // 'B'
复制代码
现在元组能够经过infer
推断其中类型,和16. FirstChar<T>
同理
type FirstItem<T extends any[]> = T extends [infer T, ...any] ? T : never
复制代码
type A = IsNever<never> // true
type B = IsNever<string> // false
type C = IsNever<undefined> // false
复制代码
粗略一看,霍,介不容易嘛?自信写出
type IsNever<T> = T extends never ? true : false
复制代码
可是呢,never
当泛型直接传入时,会直接返回never
,即IsNever<never>
返回never
和须要的true
不符合,所以行不通。
那有没有办法能够避开这个现象呢,有,和避开启动分布式条件类型的方法一致:
type Blah<T> = Box<T> extends Whatever ? A : B
type Blah<T> = Whatever extends T ? A : B
复制代码
包装T
或T
做为被继承的。
never extends T
是咱们想要的么?never
是全部的类型基类所以它继承谁都是true
,因此不符合要求。
因此咱们要选择包装T
,包装T
的方式不少种,这里选择了最简单的一种,包装成元组:
type IsNever<T> = [T] extends [never] ? true : false
复制代码
前面20题的难度说不上高,可是能提高对infer
以及一些现象和用法的理解。
github仓库地址
若是本文对你有所帮助,麻烦点个赞支持一些,谢谢:)