TS类型挑战题目记录-中等篇1
好了, 接下来到中等的挑战了, 拭目以待。
Get Return Type
- 不使用 ReturnType 实现 TypeScript 的 ReturnType
泛型。 1
2
3
4
5
6
7
8
9
10type MyReturnType<T> = T extends (...args: any) => infer R ? R : never
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
Omit
- 不使用Omit实现TypeScript的Omit<T, K> 泛型。Omit会创建一个省略 K 中字段的 T 对象。
1
2
3
4
5
6
7
8
9
10
11
12
13type MyOmit<T, K extends keyof T> = { [key in keyof T as key extends K ? never : key]: T[key] }
// as key extends K ? never : key 这部分叫做重映射
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false,
}
Readonly 2
- 实现一个通用MyReadonly2<T, K>,它带有两种类型的参数T和K。K指定应设置为Readonly的T的属性集。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly
一样。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44// = keyof T作为默认值
type MyReadonly2<T extends Record<string, any>, K extends keyof T = keyof T> = {
readonly[key in K]: T[key]
} & {
[ key in keyof T as key extends K ? never : key ]: T[key]
}
interface Todo {
title: string
description: string
completed: boolean
}
const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: "Hey",
description: "foobar",
completed: false,
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK
interface Todo1 {
title: string
description?: string
completed: boolean
}
interface Todo2 {
readonly title: string
description?: string
completed: boolean
}
interface Expected {
readonly title: string
readonly description?: string
completed: boolean
}
Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>
Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'title' | 'description'>, Expected>>
Expect<Alike<MyReadonly2<Todo2, 'description' >, Expected>>
Deep Readonly
- 实现一个通用的DeepReadonly
,它将对象的每个参数及其子对象递归地设为只读。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55type DeepReadonly<T> = { readonly [ key in keyof T ]: T[key] extends Record<string, any> ?(T[key] extends Function ? T[key] : DeepReadonly<T[key]>) : T[key] }
// 把Function情况也考虑进去了
type X1 = {
a: () => 22
b: string
c: {
d: boolean
e: {
g: {
h: {
i: true
j: 'string'
}
k: 'hello'
}
l: [
'hi',
{
m: ['hey']
},
]
}
}
}
type X2 = { a: string } | { b: number }
type cases = [
Expect<Equal<DeepReadonly<X1>, Expected1>>,
Expect<Equal<DeepReadonly<X2>, Expected2>>,
]
type Expected1 = {
readonly a: () => 22
readonly b: string
readonly c: {
readonly d: boolean
readonly e: {
readonly g: {
readonly h: {
readonly i: true
readonly j: 'string'
}
readonly k: 'hello'
}
readonly l: readonly [
'hi',
{
readonly m: readonly ['hey']
},
]
}
}
}
type Expected2 = { readonly a: string } | { readonly b: number }
Tuple to Union
- 实现泛型TupleToUnion
,它返回元组所有值的合集。 1
2
3
4
5
6type TupleToUnion<T extends any[]> = T extends Array<infer U> ? U : never
type TupleToUnion<T extends any[]> = T[number]
type Arr = ['1', '2', '3']
type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
Chainable Options
- 在 JavaScript 中我们经常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给它赋上类型吗?
在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 option(key, value) 和 get()。在 option 中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 get 获取最终结果。 - 你只需要在类型层面实现这个功能 - 不需要实现任何 TS/JS 的实际逻辑。
你可以假设 key 只接受字符串而 value 接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 key 只会被使用一次。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51type Chainable<T = {}> = {
option<K extends string, V>(key: K extends keyof T ? never : K, value: V): Chainable<Omit<T, K> & {[P in K]: V}>;
get(): T
}
/**
* 1. Chainable类型添加一个新的类型参数T,并且不能忘记默认它是一个空对象
* 2. 我们希望option(key, value)返回Chainable类型本身(我们希望有可能进行链式调用),但是要将类型信息累加到其类型参数
* 3. 当有相同K时,后面覆盖旧的
*/
declare const a: Chainable
const result1 = a
.option('foo', 123)
.option('bar', { value: 'Hello World' })
.option('name', 'type-challenges')
.get()
const result2 = a
.option('name', 'another name')
// @ts-expect-error
.option('name', 'last name')
.get()
const result3 = a
.option('name', 'another name')
// @ts-expect-error
.option('name', 123)
.get()
type cases = [
Expect<Alike<typeof result1, Expected1>>,
Expect<Alike<typeof result2, Expected2>>,
Expect<Alike<typeof result3, Expected3>>,
]
type Expected1 = {
foo: number
bar: {
value: string
}
name: string
}
type Expected2 = {
name: string
}
type Expected3 = {
name: number
}
Last of Array
- 实现一个通用Last
,它接受一个数组T并返回其最后一个元素的类型。 1
2
3
4
5
6
7type Last<T extends unknown[]> = T extends [...infer First, infer Last] ? Last : never
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
Pop
- 实现一个通用Pop
,它接受一个数组T,并返回一个由数组T的前length-1项以相同的顺序组成的数组。 1
2
3
4
5
6
7type Pop<T extends unknown[]> = T extends [...infer First, infer Last] ? [... First] : []
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]
Primise.all
- 键入函数PromiseAll,它接受PromiseLike对象数组,返回值应为Promise
,其中T是解析的结果数组。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16type MyAwaited<T> = T extends PromiseLike<infer R> ? R : T
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<{
[key in keyof T]: MyAwaited<T[key]>
}>
const promiseAllTest1 = PromiseAll([1, 2, 3] as const)
const promiseAllTest2 = PromiseAll([1, 2, Promise.resolve(3)] as const)
const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)])
const promiseAllTest4 = PromiseAll<Array<number | Promise<number>>>([1, 2, 3])
type cases = [
Expect<Equal<typeof promiseAllTest1, Promise<[1, 2, 3]>>>,
Expect<Equal<typeof promiseAllTest2, Promise<[1, 2, number]>>>,
Expect<Equal<typeof promiseAllTest3, Promise<[number, number, number]>>>,
Expect<Equal<typeof promiseAllTest4, Promise<number[]>>>,
]
Type Lookup
- 有时,您可能希望根据某个属性在联合类型中查找类型。
- 在此挑战中,我们想通过在联合类型
Cat | Dog
中搜索公共type
字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp<Dog | Cat, ‘dog’>获得Dog,LookUp<Dog | Cat, ‘cat’>获得Cat。1
2
3
4
5
6
7
8
9
10
11
12
13
14type LookUp<U extends {type: string}, T extends string> = U extends { type : T } ? U : never
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 随心所欲录!
评论