我有一个带参数的方法。我希望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,但这是我认为首先尝试并且看似合理的事情。
答案 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的答案,如果您确实想要强制执行ethnicity
和breed
是相互排他的,那么您可以使用映射类型来强制执行没有某些领域:
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