一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情。
题目二十五:append-to-object
// template.ts
type AppendToObject<T, U, V> = any
// test-cases.ts
import { Equal, Expect } from '@type-challenges/utils'
type test1 = {
key: 'cat'
value: 'green'
}
type testExpect1 = {
key: 'cat'
value: 'green'
home: boolean
}
type test2 = {
key: 'dog' | undefined
value: 'white'
sun: true
}
type testExpect2 = {
key: 'dog' | undefined
value: 'white'
sun: true
home: 1
}
type test3 = {
key: 'cow'
value: 'yellow'
sun: false
}
type testExpect3 = {
key: 'cow'
value: 'yellow'
sun: false
isMotherRussia: false | undefined
}
type cases = [
Expect<Equal<AppendToObject<test1, 'home', boolean>, testExpect1>>,
Expect<Equal<AppendToObject<test2, 'home', 1>, testExpect2>>,
Expect<Equal<AppendToObject<test3, 'isMotherRussia', false | undefined>, testExpect3>>,
]
测试用例
- 示例1
test1 与 testExpect1 对比,testExpect1 多了一个参数 :home: boolean
而 AppendToObject 方法的第二个参数 home 对应多出参数的键,第三个参数则对应多出参数的值
- 示例2
通过示例1可知对应的值可以是类型,通过示例2可知对应的值也可以是具体的数值
- 示例3
通过示例3可知对应的值可以是联合类型
代码实现
- 原代码
type AppendToObject<T, U, V> = any
T是一个object的键值对
type AppendToObject<T extends Record<string, unknown>, U, V> = any
U是一个string类型
type AppendToObject<T extends Record<string, unknown>, U extends string, V> = any
- 使用
P in keyof的方式来遍历键值对
type AppendToObject<T extends Record<string, unknown>, U extends string, V> = {
[P in keyof T]: T[P]
}
- 对
T的遍历过程中添加一项U
此时,我们完成了对 T 的遍历(此方式之前使用过);在对 T 的遍历过程中,我们需要将 U 也添加进去
type AppendToObject<T extends Record<string, unknown>, U extends string, V> = {
[P in keyof T | U]: T[P]
}
上面的代码相当于
type AppendToObject<T extends Record<string, unknown>, U extends string, V> = {
[P in ((keyof T) | U)]: T[P]
}
P 是从 keyof T 和 U 两项中取值的,其中 keyof T 又是 T 的键集合
- 对遍历的
key内容处理完成后,接着对value进行处理
type AppendToObject<T extends Record<string, unknown>, U extends string, V> = {
[P in ((keyof T) | U)]: P extends U ? V : T[P]
}
题目二十六:merge
// template.ts
type Merge<F, S> = any;
// test-cases.ts
import { Equal, Expect } from '@type-challenges/utils'
type Foo = {
a: number;
b: string;
};
type Bar = {
b: number;
c: boolean;
};
type cases = [
Expect<Equal<Merge<Foo, Bar>, {
a: number;
b: number;
c: boolean;
}>>
]
实现 Merge 函数,该函数将两个类型合并成一个类型,第二个类型的键会覆盖第一个类型的键。
代码实现:方式1
- 原代码
type Merge<F, S> = any;
- 遍历对象
type Merge<F, S> = {
[P in keyof F]: ...
};
- 遍历多个对象
type Merge<F, S> = {
[P in keyof F | keyof S]: ...
};
S的优先级更高,所以优先返回S的值
type Merge<F, S> = {
[P in keyof F | keyof S]: P extends keyof S
? S[P]
: ...
};
- 接下来处理
F的内容
type Merge<F, S> = {
[P in keyof F | keyof S]: P extends keyof S
? S[P]
: P extends keyof F
? F[P]
: never
};
代码实现:方式2
- 原代码
type Merge<F, S> = any;
- 新建一个参数
K,K是F和S中的相同项,例如测试用例中的参数b
type Merge<F, S, K extends keyof F & keyof S> = any;
- 给默认值
type Merge<F, S, K extends keyof F & keyof S = keyof F & keyof S> = any;
- 使用
Omit提取出不存在于公共项的内容
Omit:参数1(对象),参数2(key),作用是去除掉参数1中 key 为参数2 的内容
type Merge<F, S, K extends keyof F & keyof S = keyof F & keyof S> = Omit<F, K> & Omit<S, K>;
此时,已经拿到了不重复内容的联合了,如测试用例中的:{ a: number, c: boolean }
- 将重复的部分联合进去
type Merge<F, S, K extends keyof F & keyof S = keyof F & keyof S> = Omit<F, K> & Omit<S, K> & Pick<S, K>;
Pick:从 S 中取出 K
- 最后将其作为一个整体返回出去
type Merge<F, S, K extends keyof F & keyof S = keyof F & keyof S> = Omit<Omit<F, K> & Omit<S, K> & Pick<S, K>, never>;