Объединение типов TypeScript разрешается как пересечение типов

у меня есть этот фрагмент

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;
}

Если вы попробуете этот код в https://www.typescriptlang.org/play/, строка obj[key] = data; имеет следующую ошибку

Введите 'J | B' нельзя присвоить типу 'J & B'. Тип «J» не может быть присвоен типу «J & B». Свойство bar отсутствует в типе J, но необходимо в типе B.

Явно чего-то не хватает, но не могу понять чего. Есть идеи?


person rocketer    schedule 07.08.2019    source источник


Ответы (2)


Typescript не может сказать, какое свойство Cache вы пытаетесь обновить, и, если есть сомнения, он считает, что значение, которое вы пытаетесь вставить, должно соответствовать обоим. Какой J | B can't.

Вы можете немного изменить подпись функции test, чтобы использовать Partial.

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

Таким образом, Typescript может быть уверен, что пары ключ/значение, которые вы ему передаете, действительны для класса Cache.

person Morphyish    schedule 07.08.2019
comment
@rocketer Partial здесь гораздо лучший выбор, ИМО. Я бы пошел с этим, если у вас нет других ограничений. - person Titian Cernicova-Dragomir; 07.08.2019
comment
@TitianCernicova-Dragomir Я согласен, я также не хочу использовать дженерики в этом случае - person rocketer; 07.08.2019

Между key и data нет никакой связи, поэтому вы можете вызвать test с помощью test( "json", new B()), и присваивание будет недействительным.

Если вы используете параметр универсального типа (давайте назовем его K) для ключа и введите data как Cache[K], typescript разрешит назначение. (Хотя это все еще не на 100% безопасно для типов, поскольку K может быть объединением, и может возникнуть та же проблема, что описана выше):

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

person Titian Cernicova-Dragomir    schedule 07.08.2019