【发布时间】:2020-10-23 04:34:10
【问题描述】:
在 TypeScript 中,我想创建一个联合类型来表示属于一个或多个不同类型的值,类似于OpenAPI 或JSON Schema 中的oneOf。根据a previous answer on a similar question,TypeScript 中的联合运算符应该这样做,因为它表示集合联合(包括或)。但是,这与 using the in operator as a type guard 时 TypeScript 中的类型推断行为不匹配,这似乎假定值属于完全联合类型之一(析取联合或异或)。举个简单的例子,这个leads to a type error:
interface SoftwareDeveloper { code(): void; }
interface Chef { cook(): void; }
type SoftwareDeveloperOrChef = SoftwareDeveloper | Chef;
// Determined at runtime. May have the code method, the cook method, or both.
const person = { code: () => { }, cook: () => { } };
const softwareDeveloperOrChef: SoftwareDeveloperOrChef = person;
if ("code" in softwareDeveloperOrChef) {
// softwareDeveloperOrChef has inferred type SoftwareDeveloper
softwareDeveloperOrChef.code();
// Coding is hungry work
if ("cook" in softwareDeveloperOrChef) {
// Not allowed, since softwareDeveloperOrChef has inferred type never
softwareDeveloperOrChef.cook();
}
}
为了得到我想要的行为和avoid type errors,我必须手动将交集类型的所有组合添加到联合中:
type SoftwareDeveloperOrChefOrBoth = SoftwareDeveloper | Chef | (SoftwareDeveloper & Chef);
const softwareDeveloperOrChefOrBoth: SoftwareDeveloperOrChefOrBoth = person;
if ("code" in softwareDeveloperOrChefOrBoth) {
// softwareDeveloperOrChef has inferred type SoftwareDeveloper | (SoftwareDeveloper & Chef)
softwareDeveloperOrChefOrBoth.code();
if ("cook" in softwareDeveloperOrChefOrBoth) {
// Allowed, since softwareDeveloperOrChefOrBoth has inferred type SoftwareDeveloper & Chef
softwareDeveloperOrChefOrBoth.cook();
}
}
一个中间问题可能是这是否是我应该期待的行为?但是,鉴于这是已实现的行为,我实际上更感兴趣的是一种为任意数量的类型构造非析取联合类型的方法。随着类型数量的增加,手动执行会导致联合类型定义的大小呈指数增长:
type AorB = A | B | A & B;
type AorBorC = A | B | C | A & B | A & C | B & C | A & B & C;
type AOrBOrCorD = A | B | C | D | ... | B & C & D | A & B & C & D;
我可以为特定数量的参数编写泛型类型:
type AnyOf2<A, B> = A | B | A & B;
type AnyOf3<A, B, C> = AnyOf2<A, AnyOf2<B, C>>;
type AnyOf4<A, B, C, D> = ...;
但我能否使用UnionToIntersection 实用程序类型implemented here 中使用的技术创建一个类似的泛型类型,该类型采用任意数量的类型,例如联合(AnyOf<A | B>)?
【问题讨论】:
-
从示例中,不清楚为什么需要所有交集类型的组合。要摆脱 TypeScript 错误,您可以将 intersection type 定义为
type SoftwareDeveloperOrChefOrBoth = SoftwareDeveloper & Chef; -
@mancristiana:我不能使用
SoftwareDeveloper & Chef的原因是分配给变量的值是在运行时确定的,可能没有实现这两个接口。我已编辑问题以添加解释这一点的评论。
标签: typescript typescript-generics