打字稿有条件地使 属性 可选

Typescript conditionally making a property optional

假设我有一个类型

interface Definition {
    [key: string]: {
        optional: boolean;
    }
}

现在是否可以构造一个类型ValueType<T extends Definition>,对于定义

{
    foo: { optional: true },
    bar: { optional: false }
}

输出类型

{
    foo?: string;
    bar: string;
}

这可能吗?

我的第一个方法是一个由映射类型和 Omit 组成的构造,它通过将可选属性和必需属性合并到一个对象中来工作。但是,这需要一个由所有可选键组成的联合类型,我不知道该怎么做。

基本想法是:

type ValueType<T extends Definition> = 
      { [key in keyof T]?: string } 
    & Omit<{ [key in keyof T]: string }, OptionalKeys<T>>

OptionalKeys<T> 返回由所有可选键构成的上述联合类型。

您可以通过以下逻辑获得该行为:

type ValueType<T extends Record<string, { optional: boolean }>> = 
  (
    { [K in keyof T]?: string } &
    { [K in keyof T as T[K]["optional"] extends false ? K : never]-?: string }
  ) extends infer O ? { [K in keyof O]: O[K] } : never

我们创建了 optionaltrue? 的所有键以及 optionalfalse 的所有键的交集。末尾的 extends infer O ? {[K in keyof O]: O[K]} : never 使类型“更漂亮”,但在功能上无关紧要。

编辑:

感谢@jcalz。我们可以把它缩短一点,同时保持键的顺序。

Playground