gpt4 book ai didi

typescript - TypeScript 中任意数量类型的非析取联合

转载 作者:行者123 更新时间:2023-12-05 07:05:33 29 4
gpt4 key购买 nike

在 TypeScript 中,我想创建一个联合类型来表示属于一个或多个不同类型的值,类似于 oneOfOpenAPIJSON Schema .根据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> = ...;

但是我可以使用类似于 AnyOf<A | B> 中使用的技术来制作一个采用任意数量类型的类似泛型类型,例如联合 ( UnionToIntersection )实用类型 implemented here

最佳答案

出现错误的原因是使用联合类型不能正确地允许 SoftwareDeveloperOrChef两者都是 SoftwareDeveloperChef .如果我们知道对象是 SoftwareDeveloper那么我们根本没有关于 cook 的任何信息属性(property)——可以是任何东西。它不必是预期类型的​​函数。

您的 type AnyOf2<A, B> = A | B | A & B; 就快完成了,但是当我们处理对象的属性时,我们可以使用 PartialRequired以获得更好的类型。

我们希望为 SoftwareDeveloperOrChefOrBoth 实现的最终类型是cookcode都是可选函数,但至少有一个是必需的。

试试这个:

type EitherOrBoth<A, B> = Partial<A & B> & (A | B);

type SoftwareDeveloperOrChef = EitherOrBoth<SoftwareDeveloper, Chef>;

一个变化是使用Partial你不能再使用 "code" in softwareDeveloperOrChef作为类型保护,因为对象可以将属性键显式设置为 undefined .例如{code: undefined; cook: () => {};}是我们类型的有效成员。

但是这段代码 check out

const softwareDeveloperOrChef: SoftwareDeveloperOrChef = person;
if (softwareDeveloperOrChef.code) {
softwareDeveloperOrChef.code();
if (softwareDeveloperOrChef.cook) {
// this is allowed now
softwareDeveloperOrChef.cook();
}
}

编辑

根据我们在评论中的讨论,您需要单独的接口(interface) SoftwareDeveloperChef有多种方法,您希望能够确定 Chef 中的一种方法是否有效存在,Chef 的所有其他方法也将出席。

为了最大限度的类型安全,组合 SoftwareDeveloperOrChefOrBoth type 应该明确声明任何成员必须拥有 Chef 的所有方法或者没有。

此实用程序类型 Not<T>通过说 T 的任何键不能设置为 undefined 以外的任何值来处理此问题:

export type Not<T> = {
[P in keyof T]?: never;
};

请注意,仅不包括该属性并不表示无法设置它。如 linked thread 中所述, 和你的 type AorB当创建一个新的无效对象(一个具有 SoftwareDeveloper 的所有方法和一些但不是所有 Chef 的方法)时,您会得到一个错误,但在将类型分配给现有对象时不会,因为这个无效对象可分配给A因此也可以分配给 AorB .

而不是宽松的 EitherOrBoth<A,B>之前建议的类型,这里是一个更严格的强制排他性的类型:

type ExclusiveAndOr<A, B> = A & Not<B> | B & Not<A> | A & B;

type SoftwareDeveloperOrChef = ExclusiveAndOr<SoftwareDeveloper, Chef>;

有了这个类型,就不再只是假设一个人可以cook他们也可以chop .

function checkPerson( softwareDeveloperOrChef: SoftwareDeveloperOrChef ) {
if (softwareDeveloperOrChef.code) {
softwareDeveloperOrChef.code();
if (softwareDeveloperOrChef.cook) {
// should be ok
softwareDeveloperOrChef.cook();
// ok to assume all methods exist
softwareDeveloperOrChef.chop();
} else {
// should be error
softwareDeveloperOrChef.chop();
}
}
}

Typescript Playground Link

关于typescript - TypeScript 中任意数量类型的非析取联合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62698675/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com