Skip to content

Generic mapped type constrained by a type parameter has different members / index signatures when constraint of that type parameter is a union #27819

Closed
@mattmccutchen

Description

@mattmccutchen

TypeScript Version: master (b2bae85)

Search Terms: mapped type parameter member index signature constraint constrained union

Code (with noImplicitAny)

function foo1<K extends string>(arg: { [P in K]: string }) {
    // Actual: Error: Element implicitly has an 'any' type because type '{ [P in K]: string; }' has no index signature.
    return arg["test"];
}
function foo2<K extends string | number>(arg: { [P in K]: string }) {
    // Actual: No error
    return arg["test"];
}
function foo3<K extends "foo">(arg: { [P in K]: string }) {
    // Actual: Error: Property 'foo' does not exist on type '{ [P in K]: string; }'.
    return arg.foo;
}
function foo4<K extends "foo" | "bar">(arg: { [P in K]: string }) {
    // Actual: No error
    return arg.foo;
}

Expected behavior: Consistent behavior between foo1 and foo2 and between foo3 and foo4.

Actual behavior: As marked.

Playground Link: link (with noImplicitAny)

Related Issues: None found.

Looks like the cause is that this line in resolvedMappedTypeMembers in the checker is using getApparentType, which changes either string or "foo" to String (which doesn't generate an index signature) but leaves unions alone. What was the thinking here??

            const keyType = constraintType.flags & TypeFlags.InstantiableNonPrimitive ? getApparentType(constraintType) : constraintType;

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptDomain: Mapped TypesThe issue relates to mapped typesFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions