It seems to me that typescript does not recognise discriminated union types when provided without any type annotation. Am I missing something ? Is there some reason for that ?
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
When initializing object literals, Typescript will infer property types, but not as constants, since they are not readonly.
So the type of stuff
will be { status: Kind, quantity: number }
, because you can later change it to:
const stuff = {
status: Kind.B,
quantity: 2,
};
stuff.status = Kind.A;
So now it's not assignable to BType
(nor AType for that matter).
You can use as const
:
const stuff = {
status: Kind.B,
quantity: 2,
} as const;
Now the type is inferred as { readonly status: Kind.B, readonly quantity: 2}
which is always assignable to BType.
Or you could do what you did and just give it type annotation:
const stuff: BType = {
status: Kind.B,
quantity: 2,
};
stuff.status = Kind.A; // Errors