此页内容

约 344 字大约 1 分钟

2022-12-01

题目

Github: Chainable options

JavaScript 中我们经常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给它赋上类型吗?

在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, TypeClass 都行。你需要提供两个函数 option(key, value)get()。在 option 中你需要使用提供的 keyvalue 扩展当前的对象类型,通过 get 获取最终结果。

declare const config: Chainable

const result = config
  .option('foo', 123)
  .option('name', 'type-challenges')
  .option('bar', { value: 'Hello World' })
  .get()

// 期望 result 的类型是:
interface Result {
  foo: number
  name: string
  bar: {
    value: string
  }
}

你只需要在类型层面实现这个功能 - 不需要实现任何 TS/JS 的实际逻辑。

你可以假设 key 只接受字符串而 value 接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 key 只会被使用一次。

解题思路

知识点:

  • 泛型参数类型约束
  • 泛型参数默认值
  • 串联函数 返回递归类型
  • 条件类型推断
Answer
type Chainable<T extends Record<PropertyKey, unknown> = {}> = {
  option<K extends PropertyKey, V>(
    key: K extends keyof T ? (V extends T[K] ? never : K) : K, 
    value: K extends keyof T ? T[K] : V
  ): Chainable<Omit<T, K> & { [P in K]: V }>
  get(): T
}