持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第34天,点击查看活动详情
1. 引言
接着上一节中,接下来我们继续Ts中等篇的题型练习
https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md 提供的TypeScript 类型体操姿势合集题型,目的是为了让大家更好的了解TS的类型系统,编写自己的类型工具,或者单纯的享受挑战的乐趣!
2. 题型
***1. IsUnion: 实现一个类型IsUnion,它接受输入类型T,并返回T是否解析为联合类型。
type case1 = IsUnion<string> // false
type case2 = IsUnion<string|number> // true
type case3 = IsUnion<[string|number]> // false
思路: 首先如果检测类型T 为联合类型时候在extends 继承判断中会触发分布条件判断;而当我们把要检测类型转换为元祖类型时候则会使联合类型受限变成包装类型,这样在extends语句中就不会触发分布式条件类型判断,则符合题意可以鉴别是否T为联合类型。 解答:
type IsUnion<T, U = T> = T extends U ? ([U] extends [T] ? false : true) : never;
type DemoText = never extends never ? true : false
type DemoText2 = [never]
type DemoText3 = [never] extends [never] ? true : false
type Demo = IsUnion<string> // type Demo = false
type Demo2 = IsUnion<string | number> // type Demo = true
type Demo3 = IsUnion<'a' | 'b' | 'c' | 'd'> // type Demo = true
type Demo4 = IsUnion<undefined | null | void | ''> // type Demo = true
type Demo5 = IsUnion<{ a: string } | { a: number }> // type Demo = true
type Demo6 = IsUnion<{ a: string | number }> // type Demo = false
type Demo7 = IsUnion<[string | number]> // type Demo = false
type Demo8 = IsUnion<string | never> // type Demo = false
type Demo9 = IsUnion<string | unknown> // type Demo = false
type Demo10 = IsUnion<string | any> // type Demo = false
type Demo11 = IsUnion<string | 'a'> // type Demo = false
type Demo12 = IsUnion<never> // type Demo = never
****2. ReplaceKeys:实现一个类型ReplaceKeys,替换联合类型中的键,如果某个类型没有这个键,只需跳过替换,类型有三个参数。
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 Nodes = NodeA | NodeB | NodeC
type ReplacedNodes = ReplaceKeys<Nodes, 'name' | 'flag', {name: number, flag: string}> // {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.
type ReplacedNotExistKeys = ReplaceKeys<Nodes, 'name', {aa: number}> // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never
思路: 首先理解题意,第一步,先对要改造的对象(U)判空处理;第二步,该题为替换目标键值为我们要替换的值,如果不存在需要替换的键则跳过,所以第二步我们要使用映射推出一个新的对象;第三步,获取要替换的目标对象所有键(U),把当前循环的键判断是否存在与需替换键的联合对象(K)中,存在的话通过Extract 提取到在替换键值(Y)对应在值,返回;否则返回替换的目标对象(U)的值。 解答:
type ReplaceKeys<U, K, Y> = U extends U ? {
[key in keyof U]: key extends K ? Y[Extract<keyof Y,key>] : U[key]
}
: never
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
- Remove Index Signature:实现RemoveIndexSignature< T>,将索引签名从对象类型中排除。
type Foo = {
[key: string]: any;
foo(): void;
}
type A = RemoveIndexSignature<Foo> // expected { foo(): void }
思路:首先理解[key: number |number|symbol]:any 的意思为对象中的键为该对应类型,值为any任意类型,代指所有该类型业务,无特指具体值意思,本质上说的是要过滤键为string,number,symbol 的键;使用联合类型过滤对象,把不符合条件的通过设置返回键为never类型,过滤掉该对象中对应值;依次判断当前键值是否为string,number,symbol 键,是则过滤;
解答:
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K]
}
type Foo = {
[key: string]: any
foo(): void
}
type Bar = {
[key: number]: any
bar(): void
0: string
}
const foobar = Symbol('foobar')
type FooBar = {
[key: symbol]: any
[foobar](): void
}
type Baz = {
bar(): void
baz: string
}
type Demo = RemoveIndexSignature<Foo> // type Demo = {foo: () => void;}
type Demo2 = RemoveIndexSignature<Bar> // type Demo2 = {bar(): void,0: string}
type Demo3 = RemoveIndexSignature<FooBar> // type Demo3 = {[foobar]: () => void;}
type Demo4 = RemoveIndexSignature<Baz> // type Demo4 = {bar: () => void;baz: string;}