microsoft / microsoft/TypeScript

Type can't `keyof`

Open
#56,871 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 Search Terms
  • keyof
🕗 Version & Regression Information
  • This is a crash
⏯ Playground Link

No response

💻 Code
export type IsEqual<A, B> =
  (<T>() => T extends A ? 1 : 2) extends
  (<T>() => T extends B ? 1 : 2) ? true : false

export type IsNotEqual<A, B> =
  (<T>() => T extends A ? 1 : 2) extends
  (<T>() => T extends B ? 1 : 2) ? false : true

export interface Entries<X> {
  100000: [true extends (
    & IsNotEqual<X, ArrayConstructor>
    // Simplifying the problem here allows for the removal of the next line.
    & ([X] extends [ObjectConstructor] ? true : false)
  ) ? true : false, {
    check: (
      & IsNotEqual<X, ArrayConstructor>
      & ([X] extends [ObjectConstructor] ? true : false)
    ),
    x: X
  }]
  [key: number & {}]: [boolean, any]
}
type Mapping<
  X,
  E extends Entries<X> = Entries<X>
> = {
  [ K in keyof E
      as true extends (
        IsEqual<E[K & number][0], true>
      ) ? K : never
  ]: E[K & number][1]
}
type A0 = keyof Mapping<ObjectConstructor>
//   ^? type A0 = 100000
type A1 = ObjectConstructor extends infer T ? Mapping<T> : never
//   ^? type A1 = { 10000: { check: true; x: ObjectConstructor } }
type A2 = ObjectConstructor extends infer T ? keyof Mapping<T> : never
//   ^? type A2 = never
type A3 = [ObjectConstructor] extends [infer T] ? keyof Mapping<T> : never
//   ^? type A3 = never
type A4 = keyof (ObjectConstructor extends infer T ? Mapping<T> : never)
//   ^? type A4 = 11000
Output
export {};
//   ^?

Compiler Options
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "target": "ES2017",
    "jsx": "react",
    "module": "ESNext",
    "moduleResolution": "node"
  }
}

Playground Link: Provided

🙁 Actual behavior

In the case of A2, the type obtained through the operation cannot be used with keyof to obtain the Keys of the corresponding type.

🙂 Expected behavior
type A2 = 11000
Additional information about the issue

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided TypeScript Playground reproduction and compare A2 with A0–A4, focusing on keyof evaluation for the conditional and mapped types shown. Done means the crash is resolved and A2 evaluates to 11000 without changing the expected behavior of the neighboring examples.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.