Typescript: Should `keyof` string mapped type equal `keyof` string index signature?

Created on 18 Apr 2019  Β·  3Comments  Β·  Source: microsoft/TypeScript


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];
}
Question

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:

type Index = { [x: string]: number };
// IntelliSense:
// type Index = {
//    [x: string]: number;
// }

type Mapped = { [x in string]: number };
// IntelliSense:
// type Mapped = {
//    [x: string]: number;
// } 

Playground

All 3 comments

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;
// } 

Playground

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.

Was this page helpful?
0 / 5 - 0 ratings