Skip to content

Intersection type with discriminated union type that includes all possible enum values cannot accept enum type #33654

@blixt

Description

@blixt

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'.

Playground Link: https://www.typescriptlang.org/play/index.html#code/KYOwrgtgBAKgngB2FA3gKClAUgewJYhQC8UARAFb4ikA0GUAMsAIYBuyJpANi+7fQFlgAZ2HMA5hzIQRYyf0wB1YFwDGOGcTIB3Fepmk0AXzRoALomQAFZnC45mAE2L0APqigWkALliWAdLgEANxQrMxcYMC+KFAgeKoA1iDMMr7CZgBOBOJQRnluHl7Rfkj+TGzAoeGRJSj5JpjuscW+8GVCohJVYRFRMVAyXZLpWTl5BU2o9JiYraXA-spqGlUzszX9HjhmABbAmcIDeI6j2SDiofFJKWlQGee5RgDaALp5wetGn+aWUACSjlAZjwADM8MBHDY7A5nCRYiczuN8gAyKDQ+xOH6gsAgVQgnCEBDMQ7AAAUjmYZmYSIuAEpfIDgWCIVDbJjnOhMOoQBkoM8TjRPJYheRhIT3iRKdT-MIEFw8GYyaQyEKAMx0z6YMFQMnFHCgqBiwlQACERE4DxypDpnl2mRw2igAFFMg7MsrMsBVMA8OxnAQaicoNLmDatVAdWTTXhhO1ycU6ba9g6na73crAxFg8UyFAANTCpCa+g8vmbKRYADKAHkAHL+YmksnGkAlzBesxgTKEBGOIXFIUVj7GUw4vEEwix+N6yy08QMovIWMLaYd4BdntQGsAI3I3rM-grwjJ8bp-gIqkiQJPic+JiAA

Related Issues:

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions