Skip to content

Commit

Permalink
Avoid deprioritization of inferences made from mapped types with prim…
Browse files Browse the repository at this point in the history
…itive type parameter constraints
  • Loading branch information
Andarist committed Apr 25, 2023
1 parent 8749fb5 commit f050215
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -25059,9 +25059,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
applyToReturnTypes(source, target, inferFromTypes);
}

function hasPrimitiveMappedTypeParameterConstaint(mappedType: MappedType): boolean {
const constraint = mappedType.typeParameter && getConstraintOfTypeParameter(mappedType.typeParameter);
return !!(constraint && constraint.flags & TypeFlags.Primitive);
}

function inferFromIndexTypes(source: Type, target: Type) {
// Inferences across mapped type index signatures are pretty much the same a inferences to homomorphic variables
const priority = (getObjectFlags(source) & getObjectFlags(target) & ObjectFlags.Mapped) ? InferencePriority.HomomorphicMappedType : 0;
const priority = (getObjectFlags(source) & getObjectFlags(target) & ObjectFlags.Mapped) &&
!hasPrimitiveMappedTypeParameterConstaint(source as MappedType) &&
!hasPrimitiveMappedTypeParameterConstaint(target as MappedType) ?
InferencePriority.HomomorphicMappedType :
InferencePriority.None;
const indexInfos = getIndexInfosOfType(target);
if (isObjectTypeWithInferableIndex(source)) {
for (const targetInfo of indexInfos) {
Expand Down
@@ -0,0 +1,71 @@
=== tests/cases/compiler/mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/54000

function foo<T>(record: Record<string, T>, entity: T) {}
>foo : Symbol(foo, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 13))
>record : Symbol(record, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 16))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 13))
>entity : Symbol(entity, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 42))
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 13))

type StringArrayRecord = Record<string, string[]>;
>StringArrayRecord : Symbol(StringArrayRecord, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 56))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))

function test() {
>test : Symbol(test, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 4, 50))

const working: Record<string, string[]> = {};
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 7, 7))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))

foo(working, []);
>foo : Symbol(foo, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 0, 0))
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 7, 7))

const working2: StringArrayRecord = {};
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 10, 7))
>StringArrayRecord : Symbol(StringArrayRecord, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 2, 56))

foo(working2, []);
>foo : Symbol(foo, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 0, 0))
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 10, 7))
}

// showcase the same behavior with index signature

function bar<T>(record: { [k: string]: T }, entity: T) {}
>bar : Symbol(bar, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 12, 1))
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 13))
>record : Symbol(record, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 16))
>k : Symbol(k, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 27))
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 13))
>entity : Symbol(entity, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 43))
>T : Symbol(T, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 13))

type StringArrayIndexSignature = { [k: string]: string[] };
>StringArrayIndexSignature : Symbol(StringArrayIndexSignature, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 57))
>k : Symbol(k, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 18, 36))

function test2() {
>test2 : Symbol(test2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 18, 59))

const working: { [k: string]: string[] } = {};
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 21, 7))
>k : Symbol(k, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 21, 20))

bar(working, []);
>bar : Symbol(bar, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 12, 1))
>working : Symbol(working, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 21, 7))

const working2: StringArrayIndexSignature = {};
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 24, 7))
>StringArrayIndexSignature : Symbol(StringArrayIndexSignature, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 16, 57))

bar(working2, []);
>bar : Symbol(bar, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 12, 1))
>working2 : Symbol(working2, Decl(mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts, 24, 7))
}

@@ -0,0 +1,72 @@
=== tests/cases/compiler/mappedTypeWithPrimitiveTypeParameterConstraintInferencePriority.ts ===
// repro from https://github.com/microsoft/TypeScript/issues/54000

function foo<T>(record: Record<string, T>, entity: T) {}
>foo : <T>(record: Record<string, T>, entity: T) => void
>record : Record<string, T>
>entity : T

type StringArrayRecord = Record<string, string[]>;
>StringArrayRecord : { [x: string]: string[]; }

function test() {
>test : () => void

const working: Record<string, string[]> = {};
>working : Record<string, string[]>
>{} : {}

foo(working, []);
>foo(working, []) : void
>foo : <T>(record: Record<string, T>, entity: T) => void
>working : Record<string, string[]>
>[] : never[]

const working2: StringArrayRecord = {};
>working2 : StringArrayRecord
>{} : {}

foo(working2, []);
>foo(working2, []) : void
>foo : <T>(record: Record<string, T>, entity: T) => void
>working2 : StringArrayRecord
>[] : never[]
}

// showcase the same behavior with index signature

function bar<T>(record: { [k: string]: T }, entity: T) {}
>bar : <T>(record: { [k: string]: T; }, entity: T) => void
>record : { [k: string]: T; }
>k : string
>entity : T

type StringArrayIndexSignature = { [k: string]: string[] };
>StringArrayIndexSignature : { [k: string]: string[]; }
>k : string

function test2() {
>test2 : () => void

const working: { [k: string]: string[] } = {};
>working : { [k: string]: string[]; }
>k : string
>{} : {}

bar(working, []);
>bar(working, []) : void
>bar : <T>(record: { [k: string]: T; }, entity: T) => void
>working : { [k: string]: string[]; }
>[] : never[]

const working2: StringArrayIndexSignature = {};
>working2 : StringArrayIndexSignature
>{} : {}

bar(working2, []);
>bar(working2, []) : void
>bar : <T>(record: { [k: string]: T; }, entity: T) => void
>working2 : StringArrayIndexSignature
>[] : never[]
}

@@ -0,0 +1,30 @@
// @strict: true
// @noEmit: true

// repro from https://github.com/microsoft/TypeScript/issues/54000

function foo<T>(record: Record<string, T>, entity: T) {}

type StringArrayRecord = Record<string, string[]>;

function test() {
const working: Record<string, string[]> = {};
foo(working, []);

const working2: StringArrayRecord = {};
foo(working2, []);
}

// showcase the same behavior with index signature

function bar<T>(record: { [k: string]: T }, entity: T) {}

type StringArrayIndexSignature = { [k: string]: string[] };

function test2() {
const working: { [k: string]: string[] } = {};
bar(working, []);

const working2: StringArrayIndexSignature = {};
bar(working2, []);
}

0 comments on commit f050215

Please sign in to comment.