Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclusive Or and the Optional never Trick #16

Open
utterances-bot opened this issue Mar 19, 2022 · 4 comments
Open

Exclusive Or and the Optional never Trick #16

utterances-bot opened this issue Mar 19, 2022 · 4 comments

Comments

@utterances-bot
Copy link

Exclusive Or and the Optional never Trick

Effective TypeScript: Exclusive Or and the Optional never Trick

https://effectivetypescript.com/2021/11/11/optional-never/

Copy link

Thanks Dan for this blog ! :)
what about
type XOR<A, B> = Exclude<A | B, A & B>

Copy link
Owner

danvk commented Mar 20, 2022

That's a cute idea (and mathematically correct!) but unfortunately TypeScript isn't able to follow along:

type XOR<A, B> = Exclude<A | B, A & B>

type ExclusiveThing = XOR<ThingOne, ThingTwo>;
//   ^? type ExclusiveThing = ThingOne | ThingTwo

The issue is that the Exclude generic filters unions; it won't break up a "base" type like an interface or a string.

Copy link

Beraliv commented Apr 27, 2024

Hey @danvk! Thank you for mentioning ts-essentials.

This type hasn't been added to TypeScript since 2017, you can read more in microsoft/TypeScript#14094

The utility type existed in ts-essentials for a long time but it didn't have a support of variadic XOR which last year was implemented in ts-xor - microsoft/TypeScript#14094 (comment) (It's also supported in ts-essentials@10)

Copy link

userFortyTwo commented Oct 16, 2024

Here's a version that is perhaps a little more readable:

type Xor<T, U> = (T & Never<T, U>) | (U & Never<U, T>);

type Never<T, U> = {
    readonly [K in Exclude<keyof U, keyof T>]?: never;
};

Also worth noting that the definition can be easily extended to three or more types using either

type Xor3<T, U, V> = (T & Never<T, U | V>) | (U & Never<U, T | V>) | (V & Never<V, T | U>);

or, equivalently,

type Xor3<T, U, V> = Xor<Xor<T, U>, V>;

EDIT: Or, yeah, just use ts-xor or ts-essentials if you don't mind adding an additional dependency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants