一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
题目三十一:isnever
// template.ts
type IsNever = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<IsNever<never>, true>>,
Expect<Equal<IsNever<never | string>, false>>,
Expect<Equal<IsNever<''>, false>>,
Expect<Equal<IsNever<undefined>, false>>,
Expect<Equal<IsNever<null>, false>>,
Expect<Equal<IsNever<[]>, false>>,
Expect<Equal<IsNever<{}>, false>>,
]
实现一个类型 IsNever,它接受输入类型 'T'。
如果类型解析为 “never”,则返回 “true”,否则返回 “false”。
测试用例
通过测试用例可以知道:
- 传入
never则返回true - 传入其他任何内容都是
false
代码实现
- 原代码
type IsNever<T> = any
never不继承任何类型,所以这里使用[]处理
type IsNever<T> = [T] extends [never] ? true : false
题目三十二:isunion
// template.ts
type IsUnion<T> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<IsUnion<string>, false >>,
Expect<Equal<IsUnion<string|number>, true >>,
Expect<Equal<IsUnion<'a'|'b'|'c'|'d'>, true >>,
Expect<Equal<IsUnion<undefined|null|void|''>, true >>,
Expect<Equal<IsUnion<{ a: string }|{ a: number }>, true >>,
Expect<Equal<IsUnion<{ a: string|number }>, false >>,
Expect<Equal<IsUnion<[string|number]>, false >>,
// Cases where T resolves to a non-union type.
Expect<Equal<IsUnion<string|never>, false >>,
Expect<Equal<IsUnion<string|unknown>, false >>,
Expect<Equal<IsUnion<string|any>, false >>,
Expect<Equal<IsUnion<string|'a'>, false >>,
]
实现一个类型 IsUnion,它接受输入类型 T,并返回 T 是否解析为联合类型。
在条件类型中,联合将是分布的,看看下面的例子:
type Test<T, T2 = T> = T extends T2 ? {t: T, t2: T2} : true
type a = Test<string | number> // {t: string, t2: string | number} | {t: number, t2: string | number}
type b = Test<string> // {t: string, t2: string}
当分布发生时,T和T2之间存在差异。
我们可以通过将T2赋回T来判断T是否为联合类型。我们用方括号来避免分配。
代码实现
- 原代码
type IsUnion<T> = any
- 设置类型
U
type IsUnion<T, U = T> = any
- 约束
T继承于U
type IsUnion<T, U = T> = T extends U
? ...
: never
- 因为条件类型中的联合类型是分布的,使用
[]来避免分配
type IsUnion<T, U = T> = T extends U
? [U] extends [T]
? false // 两项相等,说明不是联合类型
: true
: never
题目三十三:replacekeys
// template.ts
type ReplaceKeys<U, T, Y> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'
type NodeA = {
type: 'A'
name: string
flag: number
}
type NodeB = {
type: 'B'
id: number
flag: number
}
type NodeC = {
type: 'C'
name: string
flag: number
}
type ReplacedNodeA = {
type: 'A'
name: number
flag: string
}
type ReplacedNodeB = {
type: 'B'
id: number
flag: string
}
type ReplacedNodeC = {
type: 'C'
name: number
flag: string
}
type NoNameNodeA = {
type: 'A'
flag: number
name: never
}
type NoNameNodeC = {
type: 'C'
flag: number
name: never
}
type Nodes = NodeA | NodeB | NodeC
type ReplacedNodes = ReplacedNodeA | ReplacedNodeB | ReplacedNodeC
type NodesNoName = NoNameNodeA | NoNameNodeC | NodeB
type cases = [
Expect<Equal<ReplaceKeys<Nodes, 'name' | 'flag', { name: number; flag: string }>, ReplacedNodes>>,
Expect<Equal<ReplaceKeys<Nodes, 'name', { aa: number }>, NodesNoName>>,
]
实现一个类型 ReplaceKeys,替换联合类型中的键,如果某个类型没有这个键,只需跳过替换,一个类型需要三个参数。
测试用例
Equal<ReplaceKeys<Nodes, 'name' | 'flag', { name: number; flag: string }>, ReplacedNodes>
Nodes 中将键为 name 的字段,值修改为 number;将键为 flag 的字段,值修改为 string
Equal<ReplaceKeys<Nodes, 'name', { aa: number }>, NodesNoName>
准备在 Nodes 中修改键为 name 的字段,但是参数3中没有对应的键为 name 的字段,所以就将 Nodes 中键为 name 的字段,值修改为 never
代码实现
- 原代码
U:联合类型T:需要修改的字段名Y:修改的字段值,需要和T进行匹配
type ReplaceKeys<U, T, Y> = any
- 先排除
never的情况
never 不继承自任何类型
type ReplaceKeys<U, T, Y> = U extends [never]
? never
: ...
- 遍历
U,并判断每个遍历的键是否存在于T中。如果不存在,则说明无需修改,直接原值返回即可
type ReplaceKeys<U, T, Y> = U extends [never]
? never
: {[P in keyof U]: P extends T
? ...
: U[P]}
- 如果当前循环的键存在于
T中,那么继续判断这个键是否存在于Y的键中,如果存在则使用Y的值替换,不存在则返回never
type ReplaceKeys<U, T, Y> = U extends [never]
? never
: {[P in keyof U]: P extends T
? P extends keyof Y
? Y[P]
: never
: U[P]}