Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
blixt opened this issue Sep 28, 2019 · 3 comments
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@blixt
Copy link

blixt commented Sep 28, 2019

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:

@jack-williams
Copy link
Collaborator

Looks to be a duplicate of #33243.

@RyanCavanaugh
Copy link
Member

Workaround

  const payload: Payload = { type, value };
  return { id, ...payload };

@RyanCavanaugh
Copy link
Member

Shorter

type Payload =
  | { type: "A"  }
  | { type: "B" }

declare const type: "A" | "B";
// Should be OK, but errors
const id: { id: number } & Payload = { id: 0, type };

@rbuckton I think your PR to expand out possible types of unions should make this work - any idea why that's not happening?

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript Help Wanted You can do this and removed Needs Investigation This issue needs a team member to investigate its status. Rescheduled This issue was previously scheduled to an earlier milestone labels Feb 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
Development

No branches or pull requests

5 participants