Skip to content

Unexpected behavior of enum keys while evaluating mapped types #62051

Open
@michaeldnkh

Description

@michaeldnkh

🔎 Search Terms

"enum key type","enum nested type","enum nested type resolution","enum template literal type","enum mapped type","computed property name enum type","enum type never","enum keyof","enum key nested type never","enum template literal type never"

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about enums and keys.
  • I tried version from 4.1.5 up to the latest nightly

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=4.9.5#code/C4TwDgpgBAchDOwIBMDSETwDwBUB8UAvFDlBAB5IB2y8UAhlSANoC6AUFFAPxRUQA3CACdOUAFwkylCDToB7AEYArCAGNgYrrwDeWrgeaooASypQA1hnkAzEq0nGK1WlETCzAc30GDvHEas0i4KKurAPFAABgAkOqgAvlFQAD7RcYkAdHFwiCjomLiBeEkS6fFJPr5l-EKi1VAJzFYgtvb6krUiANzs7KCQsAhIyABq9AA2AK4QuAA0UE4ycm7AHlSeBMRLIeVmNiJQAGImwogJ2Tr7hwBKw5Xax6eIwbKuLW04+ry5I+PTswCJzOwFYCzuiDwHT4ghEYkcrxWHzsX18-kC0K6wl6-XA0AAwvIqDYTJ4sPitlA9FxPBBgFgdm86L98hhsBS8AAKFqOACUkhZY0mM3JC1QeF6CRxsimAFsoABRKhygp0HSLACMRCgACJUBqdWKAEzavVGnWNPoDAna6l8OWdOWKHpiejCYSSRgsVi9LjMJUqtmZfUOVbrTy+xZGyTuLySnHsCZ0qBqIkkzy2hIMOiE4mk8kSvqpvOeTK04CcvUG3ndKAAejrUGECHkEyEdGA8igAHIsd2FhRIBoUJJu7GNt32MX02W6ZXUOaa-XG834K321BOz3x55+9npEORlO06TZxWA7LVcGNUuG02W22EJuu2O1l49-Q6IPwihjyWz5yF5Xgut4rg+G5bq+4bdqsbrAF4UA2MI8jygIAAsmQAJyZAArAsvawsIMFmFAWDEOhmQAByZGh7BAA

💻 Code

type NestedKeys<T> = T extends any[]
  ? never
  : T extends object
    ? {
        [K in keyof T]: K extends string
          ? T[K] extends object ? `${K}` | `${K}.${NestedKeys<T[K]>}` : `${K}`
          : never
      }[keyof T]
    : never;

type NestedValue<T, K extends string> = K extends `${infer First}.${infer Rest}`
  ? First extends keyof T
    ? NestedValue<T[First], Rest>
    : never
  : K extends keyof T
    ? T[K]
    : never;

type Config<C> = {
  get<K extends NestedKeys<C>>(key: K): NestedValue<C, K>;
};

enum EnumKeys { K1 = "K1", K2 = "K2" }

type C = {
  num: number;
  arr: any[];
  [EnumKeys.K1]: string;
  K2: string;
};


let config = {} as Config<C>;

config.get("K1"); // resolves to 'never', expected: 'string'
config.get("K2"); // resolves to 'string', as expected
config.get(EnumKeys.K1); // resolves to 'string', as expected
config.get(EnumKeys.K2); // resolves to 'string' starting from v4.9.5, 'never' in <= v4.8.4

🙁 Actual behavior

When using a string constant value matching the enum value in a mapped type inferring object values from their keys, the string value is not recognized as an object key when the type definition uses enum values to declare keys.
This leads to inability to safely type the possible keys' range in an object based on enum definitions.

🙂 Expected behavior

Using either enum values or strings interchangeably should not affect the way mapped types are evaluated.

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions