Typescript是否支持互斥类型?

时间:2017-02-08 20:54:33

标签: typescript

我有一个带参数的方法。我希望Typescript验证传入的对象(在typescript编译时,我理解运行时是一个不同的动物)只满足一个允许的接口。

示例:

interface Person {ethnicity: string;}
interface Pet {breed: string;}
function getOrigin(value: Person ^ Pet){...}

getOrigin({}); //Error
getOrigin({ethnicity: 'abc'}); //OK
getOrigin({breed: 'def'}); //OK
getOrigin({ethnicity: 'abc', breed: 'def'});//Error

我意识到Person ^ Pet不是有效的Typescript,但这是我认为首先尝试并且看似合理的事情。

5 个答案:

答案 0 :(得分:5)

根据this issue中的建议,您可以使用conditional types来编写XOR类型:

type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;

现在您的示例有效:

interface Person {ethnicity: string;}
interface Pet {breed: string;}
function getOrigin(value: XOR<Person, Pet>) { /* ... */}

getOrigin({}); //Error
getOrigin({ethnicity: 'abc'}); //OK
getOrigin({breed: 'def'}); //OK
getOrigin({ethnicity: 'abc', breed: 'def'});//Error

答案 1 :(得分:2)

为了增加Nitzan的答案,如果您确实想要强制执行ethnicitybreed是相互排他的,那么您可以使用映射类型来强制执行没有某些领域:

type Not<T> = {
    [P in keyof T]?: void;
};
interface Person {ethnicity: string;}
interface Pet {breed: string;}
function getOrigin(value: Person & Not<Pet>): void;
function getOrigin(value: Pet & Not<Person>): void;
function getOrigin(value: Person | Pet) { }

getOrigin({}); //Error
getOrigin({ethnicity: 'abc'}); //OK
getOrigin({breed: 'def'}); //OK

var both = {ethnicity: 'abc', breed: 'def'};
getOrigin(both);//Error

答案 2 :(得分:0)

您可以使用union types

function getOrigin(value: Person | Pet) { }

但最后一句话不会是一个错误:

getOrigin({ethnicity: 'abc', breed: 'def'}); // fine!

如果您希望这是一个错误,那么您需要使用overloading

function getOrigin(value: Pet);
function getOrigin(value: Person);
function getOrigin(value: Person | Pet) {}

答案 3 :(得分:0)

您可以使用专门用于解决此问题的微型npm软件包ts-xor

您可以执行以下操作:

import { XOR } from 'ts-xor'

interface A {
  a: string
}

interface B {
  b: string
}

let A_XOR_B: XOR<A, B>

A_XOR_B = { a: 'a' }          // OK
A_XOR_B = { b: 'b' }          // OK
A_XOR_B = { a: 'a', b: 'b' }  // fails
A_XOR_B = {}                  // fails
  

完整披露:我是这个小包装的作者。我发现我需要一直在仓库之间实现XOR类型。因此,我为我和社区发布了它,这样我也可以使用自述文件和共享的jsdoc批注正确地记录它。该实现是@Guilherme Agostinelli从社区中共享的。

答案 4 :(得分:0)

您可以使用Discriminating Unions

interface Person {
  readonly discriminator: "Person"
  ethnicity: string
}

interface Pet {
  readonly discriminator: "Pet"
  breed: string
}

function getOrigin(value: Person | Pet) { }

getOrigin({ }) // Error
getOrigin({ discriminator: "Person", ethnicity: "abc" }) // OK
getOrigin({ discriminator: "Pet", breed: "def"}) // OK
getOrigin({ discriminator: "Person", ethnicity: "abc", breed: "def"}) // Error