TypeScript Version: 3.4.1
Search Terms: mapped types keyof index signatures
Code
I understand that keyof on a string index signature returns string | number as per:
If X contains a string index signature, keyof X is a union of string, number, and the literal types representing symbol-like properties, otherwise
declare const key1: keyof { [key: string]: unknown }; // string | number
However, I want to ask why the same behaviour doesn't apply to Record<string, T> aka { [K in string]: T }:
declare const key2: keyof Record<string, unknown>; // string
I would expect keyof on the mapped type example to behave similarly to how it does with index signature example, because both objects can be used in the same way (number keys are coerced to strings):
{
// number keys are coerced to strings when writing
const obj1: { [key: string]: unknown } = {
1: 'foo',
foo: 'bar',
};
// number keys are coerced to strings when reading
obj1['1'];
obj1[1];
}
{
// number keys are coerced to strings when writing
const obj1: Record<string, unknown> = {
1: 'foo',
foo: 'bar',
};
// number keys are coerced to strings when reading
obj1['1'];
obj1[1];
}
The root cause here is #13715 and the assignability of numeric index signatures to string index signatures.
As for that behavior not applying to mapped types, well, producing different behavior is why we add different syntax.
For deeper research, start at #23592
Searching for an answer to this SO question brought me here.
Oddly enough, IntelliSense seems to hide the distinction between the mapped type and the string index signature:
type Index = { [x: string]: number };
// IntelliSense:
// type Index = {
// [x: string]: number;
// }
type Mapped = { [x in string]: number };
// IntelliSense:
// type Mapped = {
// [x: string]: number;
// }
This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.
Most helpful comment
Searching for an answer to this SO question brought me here.
Oddly enough, IntelliSense seems to hide the distinction between the mapped type and the string index signature:
Playground