是否可以在下面的示例中隐含地说明name属性的可能选项(基于key):

type TypeMap = {
  A: 1 | 2 | 3;
  B: 4 | 5 | 6;
};

type InnerType<TKey extends keyof TypeMap> = {
  key: TKey;
  name: TypeMap[TKey];
  // + other properties;
};

type OuterType = {
  x: InnerType; // Should be agnostic at this point
  y: InnerType; // but typescript requires a generic definition
};

const foo: OuterType = {
  x: { key: "A", name: 2 },
  y: { key: "B", name: 4 },
};

我能想到的最接近的结果如下所示(但我宁愿不必更改各自类型的格式):

type TypeMap = {
  A: 1 | 2 | 3;
  B: 4 | 5 | 6;
};

type InnerType = {
  data: Partial<{ [key in keyof TypeMap]: TypeMap[key] }>;
  // + other properties
};

type OuterType = {
  x: InnerType;
  y: InnerType;
};

const foo: OuterType = {
  x: { data: { A: 2 } }, // not blocking me from using `{}` or `{ A: 2, B: 4 }` on the same object
  y: { data: { B: 4 } },
};

另一种方法如下(但不基于键进行限制):

type TypeMap = {
  A: 1 | 2 | 3;
  B: 4 | 5 | 6;
};

type InnerType = {
  key: keyof TypeMap;
  name: TypeMap[keyof TypeMap];
  // + other properties;
};

type OuterType = {
  x: InnerType;
  y: InnerType;
};

const foo: OuterType = {
  x: { key: "A", name: 2 }, // allows me to use `{ key: "A", name 4 }`
  y: { key: "B", name: 4 },
};

推荐答案

您的InnerType版本是一个属性为unions的单一对象类型,这允许以您不想要的方式混合keyname.您确实希望InnerType本身是对象类型的联合,如下所示:

type InnerType = {
    key: "A";
    name: 1 | 2 | 3;
} | {
    key: "B";
    name: 4 | 5 | 6;
}

这给了你想要的行为:

const foo: OuterType = {
    x: { key: "A", name: 1 },
    y: { key: "B", name: 5 }
};

const badFoo: OuterType = {
    x: { key: "A", name: 4 }, // error! 
    // Type '{ key: "A"; name: 4; }' is not assignable to type 'InnerType'.
    // Type '4' is not assignable to type '1 | 2 | 3'.
    y: { key: "A", name: 2 }
}

您甚至可以将TypeMap写成InnerType,这样,如果TypeMap更新,它将自动更新:

type InnerType = { [K in keyof TypeMap]:
    { key: K, name: TypeMap[K] }
}[keyof TypeMap]

这个InnerType是在microsoft/TypeScript#47109中创造出来的distributive object type.它通过mapping overTypeMap的键K工作, for each 键K计算所需的对象类型,然后用keyof TypeMap计算indexing into映射的类型,从而得到所需的并集.


如果出于某种原因想要保留InnerType generic,以便InnerType<K>对应于特定的K constrainedkeyof TypeMap,您可以这样做:

type InnerType<K extends keyof TypeMap = keyof TypeMap> =
    { [P in K]:
        { key: P, name: TypeMap[P] }
    }[K]

type InnerTypeA = InnerType<"A">
/* type InnerTypeA = {
  key: "A";
  name: 1 | 2 | 3;
} */

type InnerTypeB = InnerType<"B">
/* type InnerTypeB = {
  key: "B";
  name: 4 | 5 | 6;
} */

并注意Kdefaultskeyof TypeMap,因此不带泛型类型参数的InnerType等同于完全并集:

type InnerTypeBoth = InnerType
/* type InnerTypeBoth = {
  key: "A";
  name: 1 | 2 | 3;
} | {
  key: "B";
  name: 4 | 5 | 6;
} */

因此,根据您的需要,您可以获得类泛型行为和类联盟行为.

Playground link to code

Typescript相关问答推荐

Typescript对每个属性具有约束的通用类型

类型断言添加到类型而不是替换

如何在函数参数中使用(或模仿)`success`的行为?

如何在单击停止录制按钮后停用摄像机?(使用React.js和Reaction-Media-Recorder)

Angular NgModel不更新Typescript

Angular文件上传到Spring Boot失败,多部分边界拒绝

如何通过TypeScrip中的函数防止映射类型中未能通过复杂推理的any值

使用TypeScrip5强制使用方法参数类型的实验方法修饰符

如何创建由空格连接的多个字符串的类型

避免混淆两种不同的ID类型

打字脚本中的模块化或命名空间

为什么在泛型方法中解析此promise 时会出现类型错误?

在打印脚本中将对象类型转换为数组

内联类型断言的工作原理类似于TypeScrip中的断言函数?

参数未映射泛型返回类型

TypeScript是否不知道其他函数内部的类型判断?

如何从联合类型推断函数参数?

如何通过nx找到所有受影响的项目?

显式推断元组类型

具有来自其他类型的修改键名称的 TypeScript 通用类型