Skip to content

String Indexer Assignable to Generic Mapped Type #28798

Closed
@jack-williams

Description

@jack-williams

TypeScript Version: Version 3.3.0-dev

Search Terms: string indexer generic mapped type assignable

Code
This seems like a regression (if it is a bug). I get the expected behaviour on the playground 3.1.

function tester<K extends 'a'>(x: Record<K, number>, y: { [x: string]: number }) {
    x = y; // no error: not expected
    y = x; // no error: expected
}

function tester2(x: Record<'a', number>, y: { [x: string]: number }) {
    x = y; // error: expected
    y = x; // no error: expected
}

Expected behavior:

A string indexer should not be assignable to a mapped type with a generic constraint.

Actual behavior:

String indexer appears to be assignable to generic record type.

Playground Link: playground with expected behaviour

Update. I think this was introduced by #28218.

// A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X.
if (!isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), getIndexType(source))) {
    const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target));
    const templateType = getTemplateTypeFromMappedType(target);
    if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
        return result;
    }
}

In this instance we have 'a' is related string, but a type with a key set of type string is not guaranteed to have the key a. When relating keys I think string and number should really be bottom types, so a possible fix is to filter them from the source index type:

filterType(getIndexType(source), t => !(t.flags & (TypeFlags.String | TypeFlags.Number)
// and symbol??

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions