持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
1. 引言
上一节中,我们介绍了三例比较简单的中等题型,接下来我们继续Ts中等篇的题型练习
https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md 提供的TypeScript 类型体操姿势合集题型,目的是为了让大家更好的了解TS的类型系统,编写自己的类型工具,或者单纯的享受挑战的乐趣!
2. 题型
- 实现泛型TupleToUnion,它返回元组所有值的合集。
type Arr = ['1', '2', '3']
type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
思路: 使用extends 先约束传入泛型为未知类型的元组,在使用元组的T[number] 返回每一个元组内容的联合类型 解答:
type TupleToUnion<T extends unknown[]> = T[number]
type Demo = TupleToUnion<[123, '456', true]> // type Demo = true | 123 | "456"
type Demo2 = TupleToUnion<[123]> // type Demo2 = 123
type Demo3 = TupleToUnion<[]> // type Demo3 = never
- 在 JavaScript 中我们很常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给他附上类型吗?
在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 option(key, value) 和 get()。在 option 中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 get 获取最终结果。
补充说明:
declare const config: Chainable
const result = config
.option('foo', 123)
.option('name', 'type-challenges')
.option('bar', { value: 'Hello World' })
.get()
// 期望 result 的类型是:
interface Result {
foo: number
name: string
bar: {
value: string
}
}
思路:
首先传入一个泛型形参,当类型Chainable调用时候T泛型变量无值,就把空对象赋予给T当做默认值;Chainable自定义类型 中有两个函数方法,分别为option:给对象赋值对应的键值,get:输出获取设置的对象实例。
option: 接受两个形参键名K和键值V,约束键名K为string类型,V不要限制,泛型即可。当实例调用(上述declare const config: Chainable)时,config.option(XXX,XXX).option(XXX,XXX)说明option 有默认返回对象,所以我们使用递归处理方式把T & {[P in K]: V}这样交叉合并,这样每次因为config.option递归执行Chainable 时候T变量形参都是交叉后的类型值,这样最后get输出时候就是一个完整的对象类型。
解答:
type Chainable<T = {}> = {
option: <K extends string,V>(Key: K,Value: V) => Chainable<T & {[P in K]: V}>
get: ()=> T
}
- 实现一个通用Last,它接受一个数组T并返回其最后一个元素的类型。
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
思路: 使用[...]解构配合infer关键字 解答:
type Last<T extends any[]> = T extends [...infer R,infer P] ? P : never
type Demo = Last<[3, 2, 1]> // type Demo = 1
type Demo2 = Last<[() => 123, { a: string }]> // type Demo2 = { a: string }