前言
BFE是一个前端面试及学习网站,这篇文章将带你学习并理解BFE-TS
的1-20
题,进行TS
能力进阶。
需要一定的TS
基础
效果
学习并理解这些题目可以让你的TS
能力飙升。
题目
1-15 Utility types 实现
1-15
题都是TS
的内置Utility types
,有用但简单,不过多描述。
1. implement Partial
映射类型添加 ? 标识
type Partial<T> = {
[P in keyof T]?: T[P];
};
复制代码
2. implement Required
映射类型删除 ? 标识
type Required<T> = {
[P in keyof T]-?: T[P];
};
复制代码
3. implement Readonly
映射类型添加 readonly 标识
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
复制代码
4. implement Record
一般用于快速建立简单对象类型,K 是创建对象类型的索引类型,T 则是创建对象类型的值类型
type Record<K extends keyof any, T> = {
[P in K]: T;
};
复制代码
5. implement Pick
选择部分 T 中的索引类型后将其映射成新的对象类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
复制代码
6. implement Omit
选择部分 T 中的索引类型后将其从原来的 keyof T 中剔除映射成新的对象类型
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
复制代码
7. implement Exclude
通过分布式条件类型(Distributive Conditional Types) 进行筛选,将满足条件的返回 never,因为联合类型忽略 never 从而达到剔除的作用
type Exclude<T, U> = T extends U ? never : T;
复制代码
8. implement Extract
同上,返回相反,只将满足条件的返回,不满足则被剔除
type Extract<T, U> = T extends U ? T : never;
复制代码
9. implement NonNullable
剔除 null | undefined 类型,表明非空
type NonNullable<T> = T extends null | undefined ? never : T;
复制代码
10. implement Parameters
通过 infer 推断函数参数并返回
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
复制代码
11. implement ConstructorParameters
通过 infer 推断 new 构造函数参数并返回,
TS 中通过 new (args) => any 声明这个函数被 new 调用时所需的参数和返回值
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
复制代码
12. implement ReturnType
通过 infer 推断函数返回值
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
复制代码
13. implement InstanceType
通过 infer 推断 new 后返回的类型
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
复制代码
14. implement ThisParameterType
通过 infer 推断函数的 this 类型
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;
复制代码
15. implement OmitThisParameter
因推断时自动忽略函数的 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
复制代码
16. implement FirstChar<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
复制代码
17. implement LastChar<T>
实现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'
复制代码
即通过一直拿出当前字符串类型的第一位然后判断剩下的是否为''
是的话就是最后一个了,不是就继续递归剩下的。
18. implement TupleToUnion<T>
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]
获取一个数组中元素的类型。
19. implement FirstItem<T>
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
复制代码
20. implement IsNever<T>
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仓库地址
如果本文对你有所帮助,麻烦点个赞支持一些,谢谢:)