Skip to content

Limit of 25 members when using union of mapped type #40803

Closed
@fluggo

Description

@fluggo

TypeScript Version: 4.0.2

Search Terms: 25, limit, mappings, mapped, label:"Domain: Big Unions", keyof

Expected behavior: Unions of mapped types (or keyof said mapped type) should work through assignment

Actual behavior: Compiler raises error when one keyof parameter is assigned to another keyof parameter. Removing any of the 26 entries in the mapped type solves the issue.

Related Issues: #35493 is similar; this question on StackOverflow appears to deal with the same behavior

Code

// Commenting out any of these fields fixes the error
type AWSObjectPayloads = {
  'ec2/vpc': { a: number },
  'ec2/image': { b: number },
  'ec2/subnet': { c: number },
  'ec2/security-group': { d: number },
  'ec2/instance': { e: number },
  'ec2/prefix-list': { f: number },
  'ec2/network-interface': { g: number },
  'ec2/vpc-endpoint': { h: number },
  'ec2/nat-gateway': { i: number },
  'ec2/dhcp-options': { j: number },
  'ec2/elastic-ip': { k: number },
  'ec2/flow-log': { l: number },
  'ec2/internet-gateway': { m: number },
  'ec2/network-acl': { n: number },
  'ec2/peering-connection': { o: number },
  'ec2/route-table': { p: number },
  'ec2/vpn-gateway': { q: number },
  'elb/load-balancer': { r: number },
  'elb-v2/load-balancer': { s: number },
  'elb-v2/target-group': { t: number },
  'rds/subnet-group': { u: number },
  'rds/cluster': { v: number },
  'rds/instance': { w: number },
  'redshift/subnet-group': { x: number },
  'redshift/cluster': { y: number },
  'cloudtrail/trail': { z: number },
};

interface AWSObjectImpl<Name, Desc> {
  objectType: Name;
  desc: Desc;
}

type AWSObjectTypesByName = {
  [K in keyof AWSObjectPayloads]: AWSObjectImpl<K, AWSObjectPayloads[K]>
};

type AWSObjectTypeName = keyof AWSObjectTypesByName;
type AWSObject<T extends AWSObjectTypeName = AWSObjectTypeName> = AWSObjectTypesByName[T];

// Using this definition for AWSObject corrects the assignment error, but
// eliminates the type narrowing:
//
//   type AWSObject<T extends AWSObjectTypeName = AWSObjectTypeName> = AWSObjectImpl<T, AWSObjectPayloads[T]>;

function foo(test: AWSObjectTypeName) {
  const testA: AWSObject = {
    objectType: test,
    desc: null as any,
  };

  if(testA.objectType === 'ec2/vpc')
    console.log(testA.desc.a);
}
Output
"use strict";
// Using this definition for AWSObject corrects the assignment error, but
// eliminates the type narrowing:
//
//   type AWSObject<T extends AWSObjectTypeName = AWSObjectTypeName> = AWSObjectImpl<T, AWSObjectPayloads[T]>;
function foo(test) {
    const testA = {
        objectType: test,
        desc: null,
    };
    if (testA.objectType === 'ec2/vpc')
        console.log(testA.desc.a);
}
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

Activity

added
Design LimitationConstraints of the existing architecture prevent this from being fixed
on Sep 28, 2020
RyanCavanaugh

RyanCavanaugh commented on Sep 28, 2020

@RyanCavanaugh
Member

When the source object doesn't have any it's pretty straightforward to demonstrate that this kind of initialization is rarely sound, but we try to make this particular kind of assignment work anyway by enumerating all possible values that the initializer could have, and seeing if that value would be assignable to AWSObject. There's a hardcoded limit here of 24 candidates (more typically hit when you have e.g. 2 x 3 x 3 possible values) for this process to avoid combinatorial explosions.

fluggo

fluggo commented on Sep 28, 2020

@fluggo
Author

I don't know what you mean by your first sentence. Was there a better way to achieve this same effect?

I can work around this error by casting the test parameter to "any" first, and that's no major loss, but if I'm running into a design limitation, I sure would like a way to do this correctly.

etlovett

etlovett commented on Jan 27, 2021

@etlovett

Here's another example of this limit of 25 union entries causing a false type error: #42518 / Playground.

NoriSte

NoriSte commented on Feb 12, 2021

@NoriSte

Here another example: Gist - Playground.

Thanks

elias6

elias6 commented on Mar 7, 2023

@elias6

This is a bit annoying and confusing. Is there any way to increase or remove this limit?

zaquille-oneil

zaquille-oneil commented on May 1, 2023

@zaquille-oneil

instead of having the typescript error "malfunction", can we at least have it indicate that there is some sort of a limit? We had only found out about this limit by commenting out 1 mapped type at a time until the error message went away.

13 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @NoriSte@elias6@etlovett@jozefchutka@br3nt

        Issue actions

          Limit of 25 members when using union of mapped type · Issue #40803 · microsoft/TypeScript