Данные, которые лишь частично соответствуют заданному типу объединения — почему TypeScript не жалуется?

Учитывая следующие определения типов:

type BaseItem = {
  name: string;
  purchasedAt: string;
  purchasePrice: number;
};

type AvailableItem = BaseItem;

type SoldItem = BaseItem & {
  soldAt: string;
  sellingPrice: number;
};

export type Item = AvailableItem | SoldItem;

Почему TypeScript не жалуется на следующее выражение?

const invalidItem: Item = {
  name: "foobar",
  purchasedAt: "1-1-2019",
  purchasePrice: 42,
  soldAt: "5-1-2019"
  // `sellingPrice` should be here, or `soldAt` should be absent
};

soldAt и sellingPrice должны либо оба присутствовать, либо вообще отсутствовать. Как заставить TypeScript применять этот инвариант?


person Willem-Aart    schedule 26.10.2019    source источник


Ответы (1)


Я недостаточно знаком со структурной системой типизации машинописного текста, чтобы объяснить, почему это происходит, но я не верю, что есть какой-либо способ заставить машинописный текст применять типы в том виде, в каком они у вас есть.

Способ получить желаемую безопасность типов — использовать размеченные объединения (где все типы имеют общее постоянное свойство, например, ключ kind). Следующий код выдаст ошибку в объекте invalidItem в вашем примере.

type AvailableItem = {
    kind: "base";
    name: string; 
    purchasedAt: string;
    purchasePrice: number;
}
type SoldItem = {
    kind: "sold";
    name: string; 
    purchasedAt: string;
    purchasePrice: number;
    soldAt: string;
    sellingPrice: number;
}
export type Item = AvailableItem | SoldItem;

см. https://www.typescriptlang.org/docs/handbook/advanced-types.html#compiled-unions, чтобы узнать больше о том, что можно сделать с размеченными союзами.

person jay_aye_see_kay    schedule 26.10.2019