Skip to content

Commit 8a22767

Browse files
authored
Merge pull request #19319 from Microsoft/fixMappedTypeInferenceErrors
Fix mapped type inference errors
2 parents 8adbcef + f8d9079 commit 8a22767

8 files changed

+229
-12
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10665,29 +10665,27 @@ namespace ts {
1066510665
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
1066610666
const members = createSymbolTable();
1066710667
for (const prop of properties) {
10668-
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
10669-
if (!inferredPropType) {
10668+
const propType = getTypeOfSymbol(prop);
10669+
// If any property contains context sensitive functions that have been skipped, the source type
10670+
// is incomplete and we can't infer a meaningful input type.
10671+
if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
1067010672
return undefined;
1067110673
}
1067210674
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName);
1067310675
inferredProp.checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
1067410676
inferredProp.declarations = prop.declarations;
10675-
inferredProp.type = inferredPropType;
10677+
inferredProp.type = inferTargetType(propType);
1067610678
members.set(prop.escapedName, inferredProp);
1067710679
}
1067810680
if (indexInfo) {
10679-
const inferredIndexType = inferTargetType(indexInfo.type);
10680-
if (!inferredIndexType) {
10681-
return undefined;
10682-
}
10683-
indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly);
10681+
indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
1068410682
}
1068510683
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);
1068610684

1068710685
function inferTargetType(sourceType: Type): Type {
1068810686
inference.candidates = undefined;
1068910687
inferTypes(inferences, sourceType, templateType);
10690-
return inference.candidates && getUnionType(inference.candidates, /*subtypeReduction*/ true);
10688+
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
1069110689
}
1069210690
}
1069310691

tests/baselines/reference/keyofAndIndexedAccess.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,9 @@ declare type Handlers<T> = {
11011101
[K in keyof T]: (t: T[K]) => void;
11021102
};
11031103
declare function on<T>(handlerHash: Handlers<T>): T;
1104-
declare var hashOfEmpty1: {};
1104+
declare var hashOfEmpty1: {
1105+
test: {};
1106+
};
11051107
declare var hashOfEmpty2: {
11061108
test: boolean;
11071109
};

tests/baselines/reference/keyofAndIndexedAccess.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,8 +1793,8 @@ declare function on<T>(handlerHash: Handlers<T>): T
17931793
>T : T
17941794

17951795
var hashOfEmpty1 = on({ test: () => {} }); // {}
1796-
>hashOfEmpty1 : {}
1797-
>on({ test: () => {} }) : {}
1796+
>hashOfEmpty1 : { test: {}; }
1797+
>on({ test: () => {} }) : { test: {}; }
17981798
>on : <T>(handlerHash: Handlers<T>) => T
17991799
>{ test: () => {} } : { test: () => void; }
18001800
>test : () => void
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts(9,5): error TS2345: Argument of type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to parameter of type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; } & ThisType<{ x: number; y: number; } & { bar: number; baz: {}; }>'.
2+
Type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; }'.
3+
Types of property 'computed' are incompatible.
4+
Type '{ bar(): number; baz: number; }' is not assignable to type 'ComputedOf<{ bar: number; baz: {}; }>'.
5+
Types of property 'baz' are incompatible.
6+
Type 'number' is not assignable to type '() => {}'.
7+
8+
9+
==== tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts (1 errors) ====
10+
// Repro from #19316
11+
12+
type ComputedOf<T> = {
13+
[K in keyof T]: () => T[K];
14+
}
15+
16+
declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
17+
18+
foo({
19+
~
20+
props: { x: 10, y: 20 },
21+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22+
computed: {
23+
~~~~~~~~~~~~~~~
24+
bar(): number {
25+
~~~~~~~~~~~~~~~~~~~~~~~
26+
let z = this.bar;
27+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28+
return 42;
29+
~~~~~~~~~~~~~~~~~~~~~~
30+
},
31+
~~~~~~~~~~
32+
baz: 42
33+
~~~~~~~~~~~~~~~
34+
}
35+
~~~~~
36+
});
37+
~
38+
!!! error TS2345: Argument of type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to parameter of type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; } & ThisType<{ x: number; y: number; } & { bar: number; baz: {}; }>'.
39+
!!! error TS2345: Type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; }'.
40+
!!! error TS2345: Types of property 'computed' are incompatible.
41+
!!! error TS2345: Type '{ bar(): number; baz: number; }' is not assignable to type 'ComputedOf<{ bar: number; baz: {}; }>'.
42+
!!! error TS2345: Types of property 'baz' are incompatible.
43+
!!! error TS2345: Type 'number' is not assignable to type '() => {}'.
44+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [mappedTypeInferenceErrors.ts]
2+
// Repro from #19316
3+
4+
type ComputedOf<T> = {
5+
[K in keyof T]: () => T[K];
6+
}
7+
8+
declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
9+
10+
foo({
11+
props: { x: 10, y: 20 },
12+
computed: {
13+
bar(): number {
14+
let z = this.bar;
15+
return 42;
16+
},
17+
baz: 42
18+
}
19+
});
20+
21+
22+
//// [mappedTypeInferenceErrors.js]
23+
"use strict";
24+
// Repro from #19316
25+
foo({
26+
props: { x: 10, y: 20 },
27+
computed: {
28+
bar: function () {
29+
var z = this.bar;
30+
return 42;
31+
},
32+
baz: 42
33+
}
34+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
=== tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts ===
2+
// Repro from #19316
3+
4+
type ComputedOf<T> = {
5+
>ComputedOf : Symbol(ComputedOf, Decl(mappedTypeInferenceErrors.ts, 0, 0))
6+
>T : Symbol(T, Decl(mappedTypeInferenceErrors.ts, 2, 16))
7+
8+
[K in keyof T]: () => T[K];
9+
>K : Symbol(K, Decl(mappedTypeInferenceErrors.ts, 3, 5))
10+
>T : Symbol(T, Decl(mappedTypeInferenceErrors.ts, 2, 16))
11+
>T : Symbol(T, Decl(mappedTypeInferenceErrors.ts, 2, 16))
12+
>K : Symbol(K, Decl(mappedTypeInferenceErrors.ts, 3, 5))
13+
}
14+
15+
declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
16+
>foo : Symbol(foo, Decl(mappedTypeInferenceErrors.ts, 4, 1))
17+
>P : Symbol(P, Decl(mappedTypeInferenceErrors.ts, 6, 21))
18+
>C : Symbol(C, Decl(mappedTypeInferenceErrors.ts, 6, 23))
19+
>options : Symbol(options, Decl(mappedTypeInferenceErrors.ts, 6, 27))
20+
>props : Symbol(props, Decl(mappedTypeInferenceErrors.ts, 6, 37))
21+
>P : Symbol(P, Decl(mappedTypeInferenceErrors.ts, 6, 21))
22+
>computed : Symbol(computed, Decl(mappedTypeInferenceErrors.ts, 6, 47))
23+
>ComputedOf : Symbol(ComputedOf, Decl(mappedTypeInferenceErrors.ts, 0, 0))
24+
>C : Symbol(C, Decl(mappedTypeInferenceErrors.ts, 6, 23))
25+
>ThisType : Symbol(ThisType, Decl(lib.d.ts, --, --))
26+
>P : Symbol(P, Decl(mappedTypeInferenceErrors.ts, 6, 21))
27+
>C : Symbol(C, Decl(mappedTypeInferenceErrors.ts, 6, 23))
28+
29+
foo({
30+
>foo : Symbol(foo, Decl(mappedTypeInferenceErrors.ts, 4, 1))
31+
32+
props: { x: 10, y: 20 },
33+
>props : Symbol(props, Decl(mappedTypeInferenceErrors.ts, 8, 5))
34+
>x : Symbol(x, Decl(mappedTypeInferenceErrors.ts, 9, 12))
35+
>y : Symbol(y, Decl(mappedTypeInferenceErrors.ts, 9, 19))
36+
37+
computed: {
38+
>computed : Symbol(computed, Decl(mappedTypeInferenceErrors.ts, 9, 28))
39+
40+
bar(): number {
41+
>bar : Symbol(bar, Decl(mappedTypeInferenceErrors.ts, 10, 15))
42+
43+
let z = this.bar;
44+
>z : Symbol(z, Decl(mappedTypeInferenceErrors.ts, 12, 15))
45+
>this.bar : Symbol(bar, Decl(mappedTypeInferenceErrors.ts, 10, 15))
46+
>this : Symbol(__object, Decl(mappedTypeInferenceErrors.ts, 10, 13))
47+
>bar : Symbol(bar, Decl(mappedTypeInferenceErrors.ts, 10, 15))
48+
49+
return 42;
50+
},
51+
baz: 42
52+
>baz : Symbol(baz, Decl(mappedTypeInferenceErrors.ts, 14, 10))
53+
}
54+
});
55+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
=== tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts ===
2+
// Repro from #19316
3+
4+
type ComputedOf<T> = {
5+
>ComputedOf : ComputedOf<T>
6+
>T : T
7+
8+
[K in keyof T]: () => T[K];
9+
>K : K
10+
>T : T
11+
>T : T
12+
>K : K
13+
}
14+
15+
declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
16+
>foo : <P, C>(options: { props: P; computed: ComputedOf<C>; } & ThisType<P & C>) => void
17+
>P : P
18+
>C : C
19+
>options : { props: P; computed: ComputedOf<C>; } & ThisType<P & C>
20+
>props : P
21+
>P : P
22+
>computed : ComputedOf<C>
23+
>ComputedOf : ComputedOf<T>
24+
>C : C
25+
>ThisType : ThisType<T>
26+
>P : P
27+
>C : C
28+
29+
foo({
30+
>foo({ props: { x: 10, y: 20 }, computed: { bar(): number { let z = this.bar; return 42; }, baz: 42 }}) : any
31+
>foo : <P, C>(options: { props: P; computed: ComputedOf<C>; } & ThisType<P & C>) => void
32+
>{ props: { x: 10, y: 20 }, computed: { bar(): number { let z = this.bar; return 42; }, baz: 42 }} : { props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }
33+
34+
props: { x: 10, y: 20 },
35+
>props : { x: number; y: number; }
36+
>{ x: 10, y: 20 } : { x: number; y: number; }
37+
>x : number
38+
>10 : 10
39+
>y : number
40+
>20 : 20
41+
42+
computed: {
43+
>computed : { bar(): number; baz: number; }
44+
>{ bar(): number { let z = this.bar; return 42; }, baz: 42 } : { bar(): number; baz: number; }
45+
46+
bar(): number {
47+
>bar : () => number
48+
49+
let z = this.bar;
50+
>z : () => number
51+
>this.bar : () => number
52+
>this : { bar(): number; baz: number; }
53+
>bar : () => number
54+
55+
return 42;
56+
>42 : 42
57+
58+
},
59+
baz: 42
60+
>baz : number
61+
>42 : 42
62+
}
63+
});
64+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @strict: true
2+
3+
// Repro from #19316
4+
5+
type ComputedOf<T> = {
6+
[K in keyof T]: () => T[K];
7+
}
8+
9+
declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
10+
11+
foo({
12+
props: { x: 10, y: 20 },
13+
computed: {
14+
bar(): number {
15+
let z = this.bar;
16+
return 42;
17+
},
18+
baz: 42
19+
}
20+
});

0 commit comments

Comments
 (0)