ts类型挑战【十七】

126 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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

test1testExpect1 对比,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 TU 两项中取值的,其中 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;
  • 新建一个参数 KKFS 中的相同项,例如测试用例中的参数 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>;