持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情
1. 引言
今天我们接着继续Ts基础篇的题型练习
https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md 提供的TypeScript 类型体操姿势合集题型,目的是为了让大家更好的了解TS的类型系统,编写自己的类型工具,或者单纯的享受挑战的乐趣!
接下来我们就由简单入深,开始一步步闯关学习
2. 题型
1.创建一个通用的Length,接受一个readonly的数组,返回这个数组的长度。
思路:
首先限制传入的泛型变量T 为只读的任意数组,在使用获取元祖固定长度方式"元祖['length']"
解答:
type Length<T extends readonly any[]> = T['length']
2.实现内置的Exclude <T, U>类型,但不能直接使用它本身。>从联合类型T中排除U的类型成员,来构造一个新的类型。 思路: 首先观察题型:T中排除U,所以只需要使用extends 条件类型语句从T中剔除 U中出现的类型成员 解答:
type MyExclude<T, U> = T extends U ? never : T
type Demo = MyExclude<'a' | 'b' | 'c', 'a' | 'b'> // type Demo = 'c'
type Demo2 = MyExclude<'a' | 'b' | 'c', 'a'> // type Demo2 = 'b' | 'c'
type Demo3 = MyExclude<string | number | (() => void), Function> // type Demo3 = string | number
3.假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。 测试用例
cases = [
Expect<Equal<MyAwaited<X>, string>>,
Expect<Equal<MyAwaited<Y>, { field: number }>>,
Expect<Equal<MyAwaited<Z>, string | number>>,
Expect<Equal<MyAwaited<Z1>, string | boolean>>,
]
思路: 通过extends条件判断确认泛型变量是否还是Promise对象,条件判断是的话执行递归操作,否的话直接返回泛型对象 解答:
type MyAwaited<T> = T extends Promise<infer U> ? MyAwaited<U> : T
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type Z1 = Promise<Promise<Promise<string | boolean>>>
type Demo1 = MyAwaited<X> // type Demo1 = string
type Demo2 = MyAwaited<Y> // type Demo2 = {field: number;}
type Demo3 = MyAwaited<Z> // type Demo3 = string | number
type Demo4 = MyAwaited<Z1> // type Demo4 = string | boolean
4.实现一个 IF 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 F。 C 只能是 true 或者 false, T 和 F 可以是任意类型。
解答:
type If<C extends Boolean, T, F> = C extends true ? T : F
type Demo = If<true, 'a', 'b'> // type Demo = 'a'
type Demo2 = If<false, 'a', 2> // type Demo2 = 2
5.在类型系统里实现 JavaScript 内置的 Array.concat 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。
补充说明:
type Result = Concat<[1], [2]> // expected to be [1, 2]
思路:通过extends 约束类型,在通过数组合并方法[...T,...U] 进行数组合并
解答:
type Concat<T extends any[], U extends any[]> = [...T,...U]
type Demo1 = Concat<[], []> // type Demo1 = []
type Demo2 = Concat<[], [1]> // type Demo2 = [1]
type Demo3 = Concat<[1, 2], [3, 4]> // type Demo3 = [1, 2, 3, 4]
type Demo4 = Concat<['1', 2, '3'], [false, boolean, '4']> // type Demo4 = ["1", 2, "3", false, boolean, "4"]
6.在类型系统里实现 JavaScript 的 Array.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false。
补充说明:
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be false``
测试用例:
type cases = [
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
Expect<Equal<Includes<[null], undefined>, false>>,
Expect<Equal<Includes<[undefined], null>, false>>,
]
思路: 但我们使用条件类型语句判断的时候
type Includes<T extends readonly any[], U> = U extends T[number] ? true : false
上述测试用例中
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
都不符合用例条件
这时候我们在细看题目,如果T 变量在通过条件链对比运算的过程中递归处理就可以解决上述问题 解答:
type Includes<T extends readonly any[],U> = T extends [infer F,...infer R] ?
Equal<F,U> extends true ? true : Includes<R,U>
: false