microsoft / microsoft/TypeScript

Template Literal Types derived from the type keys cannot be used as Indexed Access Types in generic contexts

Open
#59,909 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Awaiting More Feedback Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

🔎 Search Terms

template literal, indexed access, keyof, key of

🕗 Version & Regression Information
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about indexed access.
⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.6.1-rc#code/PTAEBUE8AcFMGdQHcCWAXAFqAdgVwLawBOKAxqANayTwB0AUGjLKAIKgC8oA2gIwC6AbkbNQAIU6he3ISLigAwpIDePKpABcOAgCNi-Lb1ABfYU3kARFaAAMhk8Pog2AGxcB7JLAAmoFNm9YAA8fUABDUlIERHMEBljXDy9fLm5Wbht+ABo2bgByGzzs3Lx8PSJi9IADABJlUvLjKuKxDJb8wvaG-RzW2vrdYibihTac0YKi8e5uiun+2eGcizHQFcn+WWcLFHgwt09Q-0CQ3wio+BjmOjkWHb2D5Mk07ng0EmwAc3a3j+-p37+f5rV7vIHFFazCHcBaDIjDWS3UAAOXcaFY2AAkpdcLAADzgUDBNCwAKIdw6ABWsFIaAAfJJwDC6up3AAzCDDMyibHwXEEolBElk0AU6m0hlcJn9VkcwkAMlAAApAV9QAAfbRlYga0A6FCffxoXU6dzuFywMLYXV4Ny63ABWBs-w+ACUXKRCnc+GgETQvP5hOJpO85KpNPpjOZygAokFSC5cIE8bKIDl4JAyua6VygA

💻 Code
// Types with numeric keys.
type A = [1];
type B = 1[];
type C = { [key: number]: 1 };
type D = { 0: 1 };

// Allowed indexed access types.
type Allowed = [A[0], A['0'], A[number], A[`${number}`], B[0], B['0'], B[number], B[`${number}`], C[0], C['0'], C[number], C[`${number}`], D[0], D['0']];
// Disallowed indexed access types.
type Disallowed = [A[string], B[string], C[string], D[string], D[number], D[`${number}`]];

type NotAnIssue<T extends object> = T[`${keyof T}`];
type Issue<T extends object> = T[`${keyof T & (string | number | bigint | boolean | null | undefined)}`];
type CompactIssue<T extends object> = T[`${Exclude<keyof T, symbol>}`];
🙁 Actual behavior

The template type literal generated from keyof T cannot be used as an indexed access type for T.

🙂 Expected behavior

The template type literal generated from keyof T or its restriction can be used as an indexed access type for T, since it is restricted to be a stringified version of the key type, which should behave exactly the same as the key type in this context.

Additional information about the issue

All object types with numeric keys can be accessed with the string version of that key. However, the compiler does not acknowledge this fact in the case of generics with proper restrictions on the type.

Using `${keyof T}` directly is prohibited because template literal types cannot be constructed from symbol types. However, if the type is restricted to the allowed string | number | bigint | boolean | null | undefined, TypeScript is still refusing to use keyof T for indexed access.

The issue does not happen with definite (non-generic) types and the issue also does not occur if `${Exclude<keyof T, symbol>}` is replaced with Exclude<keyof T, symbol> (which is still a problem for other use cases).

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

Reproduce the generic indexed-access cases in the linked TypeScript Playground, comparing them with the allowed and non-generic examples in the issue. Trace the compiler's handling of template literal types derived from keyof T; done means the restricted template literal forms are accepted as indexed access types without regressing the listed disallowed cases.

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
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.