Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript union types not excluded

I have an issue with union types and Exclude (playground link):

type Foo = {
  data: string;
  otherData: number;
};
type Bar = Omit<Foo,'otherData'>
// type Bar = { data: string; }; // Even this is not working
type FooBar = Foo | Bar;

type ShouldBeFoo = Exclude<FooBar, Bar>; // Not working
// ShouldBeFoo = never

type ShouldBeBar = Exclude<FooBar, Foo>; // Somehow working
// ShouldBeBar = { data: string }

Am I missing something related to union types and/or Exclude?

I also tried with TS 4.4 and 4.2 with the same result.


Note:

We discovered the issue using type guards, you can see it in the playground here.

like image 662
Volox Avatar asked Sep 10 '25 15:09

Volox


2 Answers

Exclude excludes types that are matching, not equal.

You are excluding Bar from FooBar. Bar matches Bar and Bar matches Foo. So you exclude both.

You could use something such as

type ExcludeExact<T, U> = T extends U ? U extends T ? T : never : never
like image 160
phry Avatar answered Sep 13 '25 05:09

phry


Utility types like Pick, Omit work well with interface or types with properties since the second arg looks of keyof K.

As yours is a union type, the TS compiler is unable to infer the correct behavior. You can try this


type Foo = {
    data: string;
    otherData: number;
};

type Bar = Omit<Foo, 'otherData'>

type FooBar = Foo | Bar;

type Reduce<T, K> = T extends K ? K : never;

type ShouldBeFoo = Reduce<FooBar, Foo>;
type ShouldBeBar = Reduce<FooBar, Bar>;


like image 36
Amir Saleem Avatar answered Sep 13 '25 06:09

Amir Saleem