rocketer
rocketer

Reputation: 1071

TypeScript union of type is resolved as intersection of types

I have this snippet

class J {
  constructor(public foo: number) {}
}

class B {
 constructor(public bar: string) {}
}

interface Cache {
 json?: J;
 binary?: B;
}

function test(key: "json" | "binary", data: J | B, obj: Cache) {
  obj[key] = data;
}

If you try this code in https://www.typescriptlang.org/play/, the line obj[key] = data; is having the following error

Type 'J | B' is not assignable to type 'J & B'. Type 'J' is not assignable to type 'J & B'. Property 'bar' is missing in type 'J' but required in type 'B'.

There's obviously something I am missing but I can't figure out what. Any idea?

Upvotes: 2

Views: 1588

Answers (2)

Morphyish
Morphyish

Reputation: 4072

Typescript can't tell which property of Cache you are trying to update, and when in doubt he considers that the value you are trying to insert should be able to fit both. Which the J | B can't.

You could change your test function signature slightly to use Partial

function test(update: Partial<Cache>, obj: Cache) {
    Object.assign(obj, update);
}

That way Typescript can be sure that the key/value pairs you are passing it are valid for the Cache class.

Upvotes: 3

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250036

There is no relation between key and data, so you could call test with test( "json", new B()) and the assignment would not be valid.

If you use a generic type parameter (lets call it K) for the key and type data as Cache[K], typescript will allow the assignment. (Even though it is still not 100% type safe as K could be a union and the same issue as described above could happen):

class J {
  constructor(public foo: number) {}
}

class B {
 constructor(public bar: string) {}
}

interface Cache {
 json?: J;
 binary?: B;
}

function test<K extends keyof Cache>(key: K, data: Cache[K], obj: Cache) {
  obj[key] = data;
}

Play

Upvotes: 4

Related Questions