Typescript жалуется на размеченные типы объединения, когда нет аннотации типа

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

export type Stuff = AType | BType

export type AType = { status: Kind.A; name: string }

export type BType = { status: Kind.B; quantity: number }

export enum Kind {
  A,
  B,
}

function PlayWithStuff(stuff: Stuff) {
  console.log('some stuff', stuff)
}

const stuff = {
  status: Kind.B,
  quantity: 2,
}

PlayWithStuff(stuff)
//            ^^^^^
// Argument of type '{ status: Kind; quantity: number; }' is not assignable to parameter of type 'Stuff'.
//   Type '{ status: Kind; quantity: number; }' is not assignable to type 'BType'.
//     Types of property 'status' are incompatible.
//       Type 'Kind' is not assignable to type 'Kind.B'.

const secondStuff: Stuff = {
  status: Kind.B,
  quantity: 2,
}

PlayWithStuff(secondStuff)
// OK, the type annotation on `secondStuff` fixes the problem

person Jean-Baptiste Rudant    schedule 03.07.2020    source источник


Ответы (1)


При инициализации литералов объектов Typescript выводит типы свойств, но не как константы, поскольку они не доступны только для чтения.

Таким образом, тип stuff будет { status: Kind, quantity: number }, потому что позже вы можете изменить его на:

const stuff = {
    status: Kind.B,
    quantity: 2,
};

stuff.status = Kind.A;

Так что теперь его нельзя присвоить BType (ни AType, если уж на то пошло).

Вы можете использовать as const:

const stuff = {
    status: Kind.B,
    quantity: 2,
} as const;

Теперь тип выводится как { readonly status: Kind.B, readonly quantity: 2}, который всегда можно присвоить BType.

Или вы можете сделать то, что сделали, и просто дать аннотацию типа:

const stuff: BType = {
    status: Kind.B,
    quantity: 2,
};

stuff.status = Kind.A; // Errors
person Roberto Zvjerković    schedule 03.07.2020