TypeScript中任意数量类型的非析取并集

TypeScript中任意数量类型的非析取并集,typescript,typescript-generics,Typescript,Typescript Generics,在TypeScript中,我想创建一个联合类型,表示属于一个或多个不同类型的值,类似于或中的oneOf。根据,TypeScript中的union运算符应该执行此操作,因为它表示集合union(包含或)。然而,这与TypeScript中的类型推断行为不匹配,后者似乎假设值恰好属于一个联合类型(析取联合或异或)。作为一个简单的例子,如下所示: 为了获得我想要的行为,我必须手动将交叉点类型的所有组合添加到并集: type SoftwareDeveloperOrChefOrBoth = Software

在TypeScript中,我想创建一个联合类型,表示属于一个或多个不同类型的值,类似于或中的
oneOf
。根据,TypeScript中的union运算符应该执行此操作,因为它表示集合union(包含或)。然而,这与TypeScript中的类型推断行为不匹配,后者似乎假设值恰好属于一个联合类型(析取联合或异或)。作为一个简单的例子,如下所示:

为了获得我想要的行为,我必须手动将交叉点类型的所有组合添加到并集:

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> = ...;

typeanyof2?

出现错误的原因是使用联合类型不能正确地使
softwaredeveloperchef
同时成为
SoftwareDeveloper
Chef
。如果我们知道对象是
SoftwareDeveloper
,那么我们根本没有关于
cook
属性的任何信息——它可能是任何东西。它不必是预期类型的函数


您的
类型AnyOf2已基本达到要求。从示例中,不清楚您为什么需要所有交叉口类型的组合。为了消除类型脚本错误,您可以定义一个as
type softwaredeveloperrorchefortware=SoftwareDeveloper&Chef
@mancristiana:我不能使用
SoftwareDeveloper&Chef
的原因是,分配给变量的值是在运行时确定的,可能没有实现这两个接口。我对问题进行了编辑,添加了一条解释这一点的注释。不幸的是,正如您所注意到的,使用
Partial
意味着我失去了使用类型保护的能力。例如,如果我将
chop
添加到
Chef
,并且我已经检查了
softwaredeveloperchef.cook
是否存在,我就不能指望
chop
也存在。这使得
AnyOf
解决方案更适合我。如果你假设一个函数存在于其他函数的基础上,那么你就有可能会犯错误。请记住,typescript接口并不是说您只能拥有这些属性。如果我创建一个对象
constperson={code:()=>{},cook:()=>{}
,它将可分配给
任何一个
,因为它可分配给
软件开发者
。但您可以检查它是否包括
cook
,也包括
chop
。因此,请注意,在运行时,您的对象只能是一个完整的
Chef
或一个完整的
SoftwareDeveloper
,中间不能有任何内容。请阅读。我将根据您的要求编辑我的答案。感谢您看到我打字中潜在的错误来源。然而,我的主要问题仍然是如何将这种“非析取并集”扩展到任意数量的类型,而不必创建单独的
AnyOf2
/
exclusiveandro2
AnyOf3
/
exclusiveandro3
。。。类型。
type AnyOf2<A, B> = A | B | A & B;
type AnyOf3<A, B, C> = AnyOf2<A, AnyOf2<B, C>>;
type AnyOf4<A, B, C, D> = ...;
type EitherOrBoth<A, B> = Partial<A & B> & (A | B);

type SoftwareDeveloperOrChef = EitherOrBoth<SoftwareDeveloper, Chef>;
const softwareDeveloperOrChef: SoftwareDeveloperOrChef = person;
if (softwareDeveloperOrChef.code) {
    softwareDeveloperOrChef.code();
    if (softwareDeveloperOrChef.cook) {
        // this is allowed now
        softwareDeveloperOrChef.cook();
    }
}
export type Not<T> = {
    [P in keyof T]?: never;
};
type ExclusiveAndOr<A, B> = A & Not<B> | B & Not<A> | A & B;

type SoftwareDeveloperOrChef = ExclusiveAndOr<SoftwareDeveloper, Chef>;
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();
        }
    }
}