首页 > 解决方案 > 在打字稿中构建映射的独占类型

问题描述

我有以下类型(简化):

type ValueRepresents = {
    boolean: true
    number?: false
    other?: false
} |
{
    boolean?: false
    number: true
    other?: false
} |
{
    boolean?: false
    number?: false
    other: true
}

我的实际类型有更多可能的键。有没有办法从可能的键列表中生成这种类型,以使只有一个键的值设置为 true 才有效?就像是:

type ValueTypes = "boolean" | "number" | "other"
type ValueRepresents <T extends ValueTypes> = {
    [k in ValueTypes]: k extends T ? true : false
}
const a: ValueRepresents<"boolean"> = {
    boolean: true,
    number: false,
    other: false,
}

但我的目标是能够使用:

// should pass
const a: ValueRepresents = { boolean: true }

// should pass
const a2: ValueRepresents = {
    boolean: true,
    number: false,
}

// should error
const a3: ValueRepresents = {
    boolean: true,
    number: true,
}

// should error
const a4: ValueRepresents = {}

我也尝试遵循这个答案,但尚未成功:

type ValueRepresents <T extends ValueTypes> = {
    [k in Exclude<T, ValueTypes>]?: false
} & { [k in T]: true }

标签: typescriptmapped-types

解决方案


您可以尝试创建这样的联合类型

type ValueTypes = "boolean" | "number" | "other"

type ValueRepresents = ({
    [K in ValueTypes]: Partial<Record<Exclude<ValueTypes, K>, false>> & Record<K, true>
})[ValueTypes]

打字稿游乐场


TL;博士

我不认为这个表达式有一个特定的名称。我在 Advanced Types 的文档中看到了类似的示例,但我将尝试解释它是如何工作的。

type ValueRepresents = {
    [K in ValueTypes]: Partial<Record<Exclude<ValueTypes, K>, false>> & Record<K, true>
}

创建类型等价于:

type ValueRepresents = {
  boolean: {
    boolean: true;
    number?: false;
    other?: false;
  };
  number: {
    boolean?: false;
    number: true;
    other?: false;
  };
  other: {
    boolean?: false;
    number?: true;
    other: true;
  };
};

并通过在方括号中添加联合类型,[ValueTypes]它会在另一个联合类型中提取这些(所有)键的值,相当于:

type ValueRepresents =
  | {
      boolean: true;
      number?: false;
      other?: false;
    }
  | {
      boolean?: false;
      number: true;
      other?: false;
    }
  | {
      boolean?: false;
      number?: true;
      other: true;
    };

推荐阅读