-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
In TypeScript you can create a union type discriminated by an enum
. If the union includes all possible enum
values, you can create it with a value that is typed to the enum
(not one of its values).
Given my example below, this is valid: const p: Payload = { type, value }
When you create a new type by intersecting the discriminated union type with another type definition, for example to add another field, this no longer works. It is now no longer possible to assign a value that is typed to the enum
to the new type.
So this is invalid: const p: { id: string } & Payload = { id, type, value }
The workaround I can think of is the following, but it's pretty tedious:
type Identify<P> = P extends { type: infer T; value: infer V }
? { id: string; type: T; value: V }
: never;
type IdentifiedPayload = Identify<Payload>;
TypeScript Version: 3.6.3
Search Terms: intersection discriminated union enum
Code
enum Type {
Join = "join",
Leave = "leave",
Message = "message",
Welcome = "welcome"
}
type Payload =
| { type: Type.Join; value: { nickname: string } }
| { type: Type.Leave; value: {} }
| { type: Type.Message; value: { message: string } }
| {
type: Type.Welcome;
value: { others: { id: string; nickname: string }[] };
};
type IdentifiedPayload = { id: string } & Payload;
function parse(data: string): IdentifiedPayload {
const [id, type, json] = data.split(" ", 3);
if (typeof json !== "string") throw Error("received invalid data");
if (!isType(type)) throw Error("invalid type " + type);
const value = JSON.parse(json);
// This line errors:
return { id, type, value };
// Note that TypeScript considers this valid:
// const p: Payload = { type, value };
}
function isType(type: string): type is Type {
return Object.values(Type).includes(type);
}
Expected behavior: Works
Actual behavior: Type '{ id: string; type: Type; value: any; }' is not assignable to type 'IdentifiedPayload'.
Related Issues: