@typescript-mutually-exclusive-xor-type
copy in my Without and XOR code
link to the stack overflow post I got it from
Link to the TS issue discussing it
Link to the conditional types docs mentioned in the SO post
TS missing a built type helper for easily making a union type mutually exclusive
For that, you need each type to set the excluded properties as optional never
type Without < T , U > = { [ P in Exclude <keyof T , keyof U >]?: never } ;
// A custom XOR type that declares two types to be mutually exclusive
// Used when we want to ensure we only pass one of a pair of related params to an endpoint
// Example: XOR<{ a: string }, { b: string }> becomes { a: string, b?: never } | { a?: never, b: string }
// see: https://stackoverflow.com/a/53229567/8802485
export type XOR < T , U > = T | U extends object ? ( Without < T , U > & U ) | ( Without < U , T > & T ) : T | U ;
Alternative that uses Partial
:
// see: https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types#comment123255834_53229567
// see: https://www.typescriptlang.org/play/?#code/LAKALgngDgpgBAVQHYEsD2SDSMIGcA8AKgHxwC8chcMAHmDEgCa6VwD8cA1jmgGasAuOEhgA3GACcA3KFAB6OXACSAWygS04uCiT0ANnpS4Gx0JFhwAojSgBDJkVIUqtekxZUOAbzgBtTNpIXDz8hAC6QoT+YXAAvnBCIuLSsuDQ8ADyIhm8RNR0DMxwXrG+YU7FoHB+ATrBEHyUEVY29oxE0XAAZHAACrYSYCi2evgASjAAxmgS7daTegCujDD4yOhYOARRSIsqAEaS5QA09Y1RmCfCYpLEdzIgpbsHRw+gOvQSvLaT8ADCtjAlRA1WqRgBYCEYAkixgD1iqQ+km+vzgABE0ABzYGg7S4DGYqEwuGgBEgd66ZE-eAAIRQsxxoKMdNmRNh8NS5ngAEFUCoRuQ4FkYDl8L4IacCacWYxyg8FKCAHpsVLTJC4IGTQFCXkofl6QU+cHauDQ2FxB5qjVwRhYnV8gUUI34u2m4kW0BWzWAgn2vWO4p4iFsmCnIy+t3m2JSOAKyQaCSgIA
type UnionKeys < T > = T extends T ? keyof T : never ;
// Improve intellisense
type Expand < T > = T extends T ? { [ K in keyof T ]: T [ K ] } : never ;
type OneOf < T extends {} []> = {
[ K in keyof T ]: Expand < T [ K ] & Partial < Record < Exclude < UnionKeys < T [ number ]>, keyof T [ K ]>, never >>>;
} [ number ];
interface Cat {
isCat : true ;
}
interface Dog {
isDog : true ;
}
interface Bird {
isBird : true ;
}
type Animal = OneOf <[ Cat , Dog , Bird ]>;
// ^?
const cat : Animal = { isCat : true } ;
const dog : Animal = { isDog : true } ;
const catDog : Animal = { isCat : true , isDog : true } ; // error
Simpler alternative that uses Omit
, which removes the property instead of setting it to never:
// see: https://stackoverflow.com/a/72858846/8802485
export type Either < A , B > = Omit < A , keyof B > | Omit < B , keyof A >;
The problem with this is that both properties in the pair can be undefined
at the same time. We want only one to be (when going the “include but set to undefined” route):
export type PerturbationsEndpointParams = {
concentration : string ;
concentrationSf : string ;
filterTags : string ;
geneExpressionThreshold : string | undefined ; // must include, but ok if value is undefined
groupLabel : string ;
ignoreGeneThreshold : ' true ' | ' false ' ;
includeControlWells : ' true ' | ' false ' ;
pairwise : ' true ' | ' false ' ;
perturbations : string ;
pvalue : string | undefined ; // must include, but ok if value is undefined
pvalueByPertType : string | undefined ; // must include, but ok if value is undefined
search : string ;
splitBy : string ;
url ?: string ;
zfpkmByTimepoint : string | undefined ; // must include, but ok if value is undefined
} ;
Inbox