Merge pull request #12640 from Microsoft/mappedTypesSecondaryInferences
Classify mapped type inferences as secondary
This commit is contained in:
commit
23992ba106
|
@ -8706,12 +8706,15 @@ namespace ts {
|
||||||
if (constraintType.flags & TypeFlags.Index) {
|
if (constraintType.flags & TypeFlags.Index) {
|
||||||
// We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X },
|
// We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X },
|
||||||
// where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source
|
// where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source
|
||||||
// type and then infer from that type to T.
|
// type and then make a secondary inference from that type to T. We make a secondary inference
|
||||||
|
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
|
||||||
const index = indexOf(typeVariables, (<IndexType>constraintType).type);
|
const index = indexOf(typeVariables, (<IndexType>constraintType).type);
|
||||||
if (index >= 0 && !typeInferences[index].isFixed) {
|
if (index >= 0 && !typeInferences[index].isFixed) {
|
||||||
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
|
const inferredType = inferTypeForHomomorphicMappedType(source, <MappedType>target);
|
||||||
if (inferredType) {
|
if (inferredType) {
|
||||||
|
inferiority++;
|
||||||
inferFromTypes(inferredType, typeVariables[index]);
|
inferFromTypes(inferredType, typeVariables[index]);
|
||||||
|
inferiority--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -143,7 +143,14 @@ var g1 = applySpec({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } }
|
// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } }
|
||||||
var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } });
|
var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } });
|
||||||
|
|
||||||
|
// Repro from #12633
|
||||||
|
|
||||||
|
const foo = <T>(object: T, partial: Partial<T>) => object;
|
||||||
|
let o = {a: 5, b: 7};
|
||||||
|
foo(o, {b: 9});
|
||||||
|
o = foo(o, {b: 9});
|
||||||
|
|
||||||
//// [isomorphicMappedTypeInference.js]
|
//// [isomorphicMappedTypeInference.js]
|
||||||
function box(x) {
|
function box(x) {
|
||||||
|
@ -244,6 +251,11 @@ var g1 = applySpec({
|
||||||
});
|
});
|
||||||
// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } }
|
// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } }
|
||||||
var g2 = applySpec({ foo: { bar: { baz: function (x) { return true; } } } });
|
var g2 = applySpec({ foo: { bar: { baz: function (x) { return true; } } } });
|
||||||
|
// Repro from #12633
|
||||||
|
var foo = function (object, partial) { return object; };
|
||||||
|
var o = { a: 5, b: 7 };
|
||||||
|
foo(o, { b: 9 });
|
||||||
|
o = foo(o, { b: 9 });
|
||||||
|
|
||||||
|
|
||||||
//// [isomorphicMappedTypeInference.d.ts]
|
//// [isomorphicMappedTypeInference.d.ts]
|
||||||
|
@ -311,3 +323,8 @@ declare var g2: (...args: any[]) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
declare const foo: <T>(object: T, partial: Partial<T>) => T;
|
||||||
|
declare let o: {
|
||||||
|
a: number;
|
||||||
|
b: number;
|
||||||
|
};
|
||||||
|
|
|
@ -459,3 +459,31 @@ var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } });
|
||||||
>baz : Symbol(baz, Decl(isomorphicMappedTypeInference.ts, 144, 34))
|
>baz : Symbol(baz, Decl(isomorphicMappedTypeInference.ts, 144, 34))
|
||||||
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 144, 41))
|
>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 144, 41))
|
||||||
|
|
||||||
|
// Repro from #12633
|
||||||
|
|
||||||
|
const foo = <T>(object: T, partial: Partial<T>) => object;
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 148, 5))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 148, 13))
|
||||||
|
>object : Symbol(object, Decl(isomorphicMappedTypeInference.ts, 148, 16))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 148, 13))
|
||||||
|
>partial : Symbol(partial, Decl(isomorphicMappedTypeInference.ts, 148, 26))
|
||||||
|
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
|
||||||
|
>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 148, 13))
|
||||||
|
>object : Symbol(object, Decl(isomorphicMappedTypeInference.ts, 148, 16))
|
||||||
|
|
||||||
|
let o = {a: 5, b: 7};
|
||||||
|
>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3))
|
||||||
|
>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 149, 9))
|
||||||
|
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 149, 14))
|
||||||
|
|
||||||
|
foo(o, {b: 9});
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 148, 5))
|
||||||
|
>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3))
|
||||||
|
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 150, 8))
|
||||||
|
|
||||||
|
o = foo(o, {b: 9});
|
||||||
|
>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3))
|
||||||
|
>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 148, 5))
|
||||||
|
>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3))
|
||||||
|
>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 151, 12))
|
||||||
|
|
||||||
|
|
|
@ -546,3 +546,42 @@ var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } });
|
||||||
>x : any
|
>x : any
|
||||||
>true : true
|
>true : true
|
||||||
|
|
||||||
|
// Repro from #12633
|
||||||
|
|
||||||
|
const foo = <T>(object: T, partial: Partial<T>) => object;
|
||||||
|
>foo : <T>(object: T, partial: Partial<T>) => T
|
||||||
|
><T>(object: T, partial: Partial<T>) => object : <T>(object: T, partial: Partial<T>) => T
|
||||||
|
>T : T
|
||||||
|
>object : T
|
||||||
|
>T : T
|
||||||
|
>partial : Partial<T>
|
||||||
|
>Partial : Partial<T>
|
||||||
|
>T : T
|
||||||
|
>object : T
|
||||||
|
|
||||||
|
let o = {a: 5, b: 7};
|
||||||
|
>o : { a: number; b: number; }
|
||||||
|
>{a: 5, b: 7} : { a: number; b: number; }
|
||||||
|
>a : number
|
||||||
|
>5 : 5
|
||||||
|
>b : number
|
||||||
|
>7 : 7
|
||||||
|
|
||||||
|
foo(o, {b: 9});
|
||||||
|
>foo(o, {b: 9}) : { a: number; b: number; }
|
||||||
|
>foo : <T>(object: T, partial: Partial<T>) => T
|
||||||
|
>o : { a: number; b: number; }
|
||||||
|
>{b: 9} : { b: number; }
|
||||||
|
>b : number
|
||||||
|
>9 : 9
|
||||||
|
|
||||||
|
o = foo(o, {b: 9});
|
||||||
|
>o = foo(o, {b: 9}) : { a: number; b: number; }
|
||||||
|
>o : { a: number; b: number; }
|
||||||
|
>foo(o, {b: 9}) : { a: number; b: number; }
|
||||||
|
>foo : <T>(object: T, partial: Partial<T>) => T
|
||||||
|
>o : { a: number; b: number; }
|
||||||
|
>{b: 9} : { b: number; }
|
||||||
|
>b : number
|
||||||
|
>9 : 9
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,15 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(60,9): error TS2403: Su
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(61,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(61,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'.
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(62,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P] | undefined; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(62,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P] | undefined; }'.
|
||||||
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'.
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'.
|
||||||
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(76,45): error TS2345: Argument of type '{ x: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'.
|
||||||
|
Property 'y' is missing in type '{ x: number; }'.
|
||||||
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(78,59): error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'.
|
||||||
|
Object literal may only specify known properties, and 'z' does not exist in type 'Readonly<{ x: number; y: number; }>'.
|
||||||
|
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(84,58): error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Partial<{ x: number; y: number; }>'.
|
||||||
|
Object literal may only specify known properties, and 'z' does not exist in type 'Partial<{ x: number; y: number; }>'.
|
||||||
|
|
||||||
|
|
||||||
==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (14 errors) ====
|
==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (17 errors) ====
|
||||||
|
|
||||||
interface Shape {
|
interface Shape {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -126,4 +132,30 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Su
|
||||||
var x: { [P in keyof T]: T[P][] }; // Error
|
var x: { [P in keyof T]: T[P][] }; // Error
|
||||||
~
|
~
|
||||||
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'.
|
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that inferences to mapped types are secondary
|
||||||
|
|
||||||
|
declare function objAndReadonly<T>(primary: T, secondary: Readonly<T>): T;
|
||||||
|
declare function objAndPartial<T>(primary: T, secondary: Partial<T>): T;
|
||||||
|
|
||||||
|
function f20() {
|
||||||
|
let x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error
|
||||||
|
~~~~~~~~
|
||||||
|
!!! error TS2345: Argument of type '{ x: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'.
|
||||||
|
!!! error TS2345: Property 'y' is missing in type '{ x: number; }'.
|
||||||
|
let x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
let x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
|
~~~~
|
||||||
|
!!! error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'.
|
||||||
|
!!! error TS2345: Object literal may only specify known properties, and 'z' does not exist in type 'Readonly<{ x: number; y: number; }>'.
|
||||||
|
}
|
||||||
|
|
||||||
|
function f21() {
|
||||||
|
let x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 });
|
||||||
|
let x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
let x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
|
~~~~
|
||||||
|
!!! error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Partial<{ x: number; y: number; }>'.
|
||||||
|
!!! error TS2345: Object literal may only specify known properties, and 'z' does not exist in type 'Partial<{ x: number; y: number; }>'.
|
||||||
}
|
}
|
|
@ -66,6 +66,23 @@ function f11<T>() {
|
||||||
function f12<T>() {
|
function f12<T>() {
|
||||||
var x: { [P in keyof T]: T[P] };
|
var x: { [P in keyof T]: T[P] };
|
||||||
var x: { [P in keyof T]: T[P][] }; // Error
|
var x: { [P in keyof T]: T[P][] }; // Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that inferences to mapped types are secondary
|
||||||
|
|
||||||
|
declare function objAndReadonly<T>(primary: T, secondary: Readonly<T>): T;
|
||||||
|
declare function objAndPartial<T>(primary: T, secondary: Partial<T>): T;
|
||||||
|
|
||||||
|
function f20() {
|
||||||
|
let x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error
|
||||||
|
let x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
let x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
|
}
|
||||||
|
|
||||||
|
function f21() {
|
||||||
|
let x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 });
|
||||||
|
let x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
let x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
}
|
}
|
||||||
|
|
||||||
//// [mappedTypeErrors.js]
|
//// [mappedTypeErrors.js]
|
||||||
|
@ -97,6 +114,16 @@ function f12() {
|
||||||
var x;
|
var x;
|
||||||
var x; // Error
|
var x; // Error
|
||||||
}
|
}
|
||||||
|
function f20() {
|
||||||
|
var x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error
|
||||||
|
var x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
var x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
|
}
|
||||||
|
function f21() {
|
||||||
|
var x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 });
|
||||||
|
var x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
var x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//// [mappedTypeErrors.d.ts]
|
//// [mappedTypeErrors.d.ts]
|
||||||
|
@ -137,3 +164,7 @@ declare function f4<T extends keyof Named>(x: T): void;
|
||||||
declare function f10<T>(): void;
|
declare function f10<T>(): void;
|
||||||
declare function f11<T>(): void;
|
declare function f11<T>(): void;
|
||||||
declare function f12<T>(): void;
|
declare function f12<T>(): void;
|
||||||
|
declare function objAndReadonly<T>(primary: T, secondary: Readonly<T>): T;
|
||||||
|
declare function objAndPartial<T>(primary: T, secondary: Partial<T>): T;
|
||||||
|
declare function f20(): void;
|
||||||
|
declare function f21(): void;
|
||||||
|
|
|
@ -145,4 +145,11 @@ var g1 = applySpec({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } }
|
// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } }
|
||||||
var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } });
|
var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } });
|
||||||
|
|
||||||
|
// Repro from #12633
|
||||||
|
|
||||||
|
const foo = <T>(object: T, partial: Partial<T>) => object;
|
||||||
|
let o = {a: 5, b: 7};
|
||||||
|
foo(o, {b: 9});
|
||||||
|
o = foo(o, {b: 9});
|
|
@ -67,4 +67,21 @@ function f11<T>() {
|
||||||
function f12<T>() {
|
function f12<T>() {
|
||||||
var x: { [P in keyof T]: T[P] };
|
var x: { [P in keyof T]: T[P] };
|
||||||
var x: { [P in keyof T]: T[P][] }; // Error
|
var x: { [P in keyof T]: T[P][] }; // Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that inferences to mapped types are secondary
|
||||||
|
|
||||||
|
declare function objAndReadonly<T>(primary: T, secondary: Readonly<T>): T;
|
||||||
|
declare function objAndPartial<T>(primary: T, secondary: Partial<T>): T;
|
||||||
|
|
||||||
|
function f20() {
|
||||||
|
let x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error
|
||||||
|
let x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
let x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
|
}
|
||||||
|
|
||||||
|
function f21() {
|
||||||
|
let x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 });
|
||||||
|
let x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 });
|
||||||
|
let x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error
|
||||||
}
|
}
|
Loading…
Reference in a new issue