Open SmolPatat opened 1 month ago
What's the use of being able to write this, if it were allowed?
I came across this issue when writing stricter types for Object.keys(...)
and Object.values(...)
.
They are
type KeysOf<T extends object> = `${keyof T & (string | number)}`; // Simplified.
and
type ValuesOf<T extends object> = T[KeysOf<T>]; // Simplified.
respectively.
The issue is that KeysOf<T>
cannot be used to index type T
even though the type is guaranteed to contain only valid keys, which could be algorithmically deduced from the definition.
The fundamental issue here is consistency. For any concrete type X
, if another type Y
can be used to index it as X[Y]
, then the expression X[`${Y}`]
(if valid) always gives the same resulting type as X[Y]
.
This bug report / suggestion is about keeping this behaviour consistent when extending the same rule to generic types. Since there are no types that present an exception to the rule, it should work with generics as well.
🔎 Search Terms
template literal, indexed access, keyof, key of
🕗 Version & Regression Information
⏯ 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
🙁 Actual behavior
The template type literal generated from
keyof T
cannot be used as an indexed access type forT
.🙂 Expected behavior
The template type literal generated from
keyof T
or its restriction can be used as an indexed access type forT
, 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 fromsymbol
types. However, if the type is restricted to the allowedstring | number | bigint | boolean | null | undefined
, TypeScript is still refusing to usekeyof 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 withExclude<keyof T, symbol>
(which is still a problem for other use cases).