Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 06cfe51

Browse files
authored
Merge pull request microsoft#17430 from Microsoft/inferenceExcessProperties
Fix inference with excess properties
2 parents e7e6475 + a14144b commit 06cfe51

File tree

5 files changed

+247
-12
lines changed

5 files changed

+247
-12
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15247,17 +15247,17 @@ namespace ts {
1524715247
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
1524815248
// Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
1524915249
const paramType = getTypeAtPosition(signature, i);
15250-
let argType = getEffectiveArgumentType(node, i);
15251-
15252-
// If the effective argument type is 'undefined', there is no synthetic type
15253-
// for the argument. In that case, we should check the argument.
15254-
if (argType === undefined) {
15255-
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
15256-
}
15257-
15250+
// If the effective argument type is undefined, there is no synthetic type for the argument.
15251+
// In that case, we should check the argument.
15252+
const argType = getEffectiveArgumentType(node, i) ||
15253+
checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
15254+
// If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter),
15255+
// we obtain the regular type of any object literal arguments because we may not have inferred complete
15256+
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
15257+
const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType;
1525815258
// Use argument expression as error location when reporting errors
1525915259
const errorNode = reportErrors ? getEffectiveArgumentErrorNode(node, i, arg) : undefined;
15260-
if (!checkTypeRelatedTo(argType, paramType, relation, errorNode, headMessage)) {
15260+
if (!checkTypeRelatedTo(checkArgType, paramType, relation, errorNode, headMessage)) {
1526115261
return false;
1526215262
}
1526315263
}
@@ -15629,6 +15629,7 @@ namespace ts {
1562915629
// For a decorator, no arguments are susceptible to contextual typing due to the fact
1563015630
// decorators are applied to a declaration by the emitter, and not to an expression.
1563115631
let excludeArgument: boolean[];
15632+
let excludeCount = 0;
1563215633
if (!isDecorator) {
1563315634
// We do not need to call `getEffectiveArgumentCount` here as it only
1563415635
// applies when calculating the number of arguments for a decorator.
@@ -15638,6 +15639,7 @@ namespace ts {
1563815639
excludeArgument = new Array(args.length);
1563915640
}
1564015641
excludeArgument[i] = true;
15642+
excludeCount++;
1564115643
}
1564215644
}
1564315645
}
@@ -15812,12 +15814,17 @@ namespace ts {
1581215814
candidateForArgumentError = candidate;
1581315815
break;
1581415816
}
15815-
const index = excludeArgument ? indexOf(excludeArgument, /*value*/ true) : -1;
15816-
if (index < 0) {
15817+
if (excludeCount === 0) {
1581715818
candidates[candidateIndex] = candidate;
1581815819
return candidate;
1581915820
}
15820-
excludeArgument[index] = false;
15821+
excludeCount--;
15822+
if (excludeCount > 0) {
15823+
excludeArgument[indexOf(excludeArgument, /*value*/ true)] = false;
15824+
}
15825+
else {
15826+
excludeArgument = undefined;
15827+
}
1582115828
}
1582215829
}
1582315830

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//// [typeInferenceWithExcessProperties.ts]
2+
// Repro from #17041
3+
4+
interface Named {
5+
name: string;
6+
}
7+
8+
function parrot<T extends Named>(obj: T): T {
9+
return obj;
10+
}
11+
12+
13+
parrot({
14+
name: "TypeScript",
15+
});
16+
17+
parrot({
18+
name: "TypeScript",
19+
age: 5,
20+
});
21+
22+
parrot({
23+
name: "TypeScript",
24+
age: function () { },
25+
});
26+
27+
parrot({
28+
name: "TypeScript",
29+
sayHello() {
30+
},
31+
});
32+
33+
34+
//// [typeInferenceWithExcessProperties.js]
35+
// Repro from #17041
36+
function parrot(obj) {
37+
return obj;
38+
}
39+
parrot({
40+
name: "TypeScript"
41+
});
42+
parrot({
43+
name: "TypeScript",
44+
age: 5
45+
});
46+
parrot({
47+
name: "TypeScript",
48+
age: function () { }
49+
});
50+
parrot({
51+
name: "TypeScript",
52+
sayHello: function () {
53+
}
54+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
=== tests/cases/compiler/typeInferenceWithExcessProperties.ts ===
2+
// Repro from #17041
3+
4+
interface Named {
5+
>Named : Symbol(Named, Decl(typeInferenceWithExcessProperties.ts, 0, 0))
6+
7+
name: string;
8+
>name : Symbol(Named.name, Decl(typeInferenceWithExcessProperties.ts, 2, 17))
9+
}
10+
11+
function parrot<T extends Named>(obj: T): T {
12+
>parrot : Symbol(parrot, Decl(typeInferenceWithExcessProperties.ts, 4, 1))
13+
>T : Symbol(T, Decl(typeInferenceWithExcessProperties.ts, 6, 16))
14+
>Named : Symbol(Named, Decl(typeInferenceWithExcessProperties.ts, 0, 0))
15+
>obj : Symbol(obj, Decl(typeInferenceWithExcessProperties.ts, 6, 33))
16+
>T : Symbol(T, Decl(typeInferenceWithExcessProperties.ts, 6, 16))
17+
>T : Symbol(T, Decl(typeInferenceWithExcessProperties.ts, 6, 16))
18+
19+
return obj;
20+
>obj : Symbol(obj, Decl(typeInferenceWithExcessProperties.ts, 6, 33))
21+
}
22+
23+
24+
parrot({
25+
>parrot : Symbol(parrot, Decl(typeInferenceWithExcessProperties.ts, 4, 1))
26+
27+
name: "TypeScript",
28+
>name : Symbol(name, Decl(typeInferenceWithExcessProperties.ts, 11, 8))
29+
30+
});
31+
32+
parrot({
33+
>parrot : Symbol(parrot, Decl(typeInferenceWithExcessProperties.ts, 4, 1))
34+
35+
name: "TypeScript",
36+
>name : Symbol(name, Decl(typeInferenceWithExcessProperties.ts, 15, 8))
37+
38+
age: 5,
39+
>age : Symbol(age, Decl(typeInferenceWithExcessProperties.ts, 16, 23))
40+
41+
});
42+
43+
parrot({
44+
>parrot : Symbol(parrot, Decl(typeInferenceWithExcessProperties.ts, 4, 1))
45+
46+
name: "TypeScript",
47+
>name : Symbol(name, Decl(typeInferenceWithExcessProperties.ts, 20, 8))
48+
49+
age: function () { },
50+
>age : Symbol(age, Decl(typeInferenceWithExcessProperties.ts, 21, 23))
51+
52+
});
53+
54+
parrot({
55+
>parrot : Symbol(parrot, Decl(typeInferenceWithExcessProperties.ts, 4, 1))
56+
57+
name: "TypeScript",
58+
>name : Symbol(name, Decl(typeInferenceWithExcessProperties.ts, 25, 8))
59+
60+
sayHello() {
61+
>sayHello : Symbol(sayHello, Decl(typeInferenceWithExcessProperties.ts, 26, 23))
62+
63+
},
64+
});
65+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
=== tests/cases/compiler/typeInferenceWithExcessProperties.ts ===
2+
// Repro from #17041
3+
4+
interface Named {
5+
>Named : Named
6+
7+
name: string;
8+
>name : string
9+
}
10+
11+
function parrot<T extends Named>(obj: T): T {
12+
>parrot : <T extends Named>(obj: T) => T
13+
>T : T
14+
>Named : Named
15+
>obj : T
16+
>T : T
17+
>T : T
18+
19+
return obj;
20+
>obj : T
21+
}
22+
23+
24+
parrot({
25+
>parrot({ name: "TypeScript",}) : { name: string; }
26+
>parrot : <T extends Named>(obj: T) => T
27+
>{ name: "TypeScript",} : { name: string; }
28+
29+
name: "TypeScript",
30+
>name : string
31+
>"TypeScript" : "TypeScript"
32+
33+
});
34+
35+
parrot({
36+
>parrot({ name: "TypeScript", age: 5,}) : { name: string; age: number; }
37+
>parrot : <T extends Named>(obj: T) => T
38+
>{ name: "TypeScript", age: 5,} : { name: string; age: number; }
39+
40+
name: "TypeScript",
41+
>name : string
42+
>"TypeScript" : "TypeScript"
43+
44+
age: 5,
45+
>age : number
46+
>5 : 5
47+
48+
});
49+
50+
parrot({
51+
>parrot({ name: "TypeScript", age: function () { },}) : { name: string; age: () => void; }
52+
>parrot : <T extends Named>(obj: T) => T
53+
>{ name: "TypeScript", age: function () { },} : { name: string; age: () => void; }
54+
55+
name: "TypeScript",
56+
>name : string
57+
>"TypeScript" : "TypeScript"
58+
59+
age: function () { },
60+
>age : () => void
61+
>function () { } : () => void
62+
63+
});
64+
65+
parrot({
66+
>parrot({ name: "TypeScript", sayHello() { },}) : { name: string; sayHello(): void; }
67+
>parrot : <T extends Named>(obj: T) => T
68+
>{ name: "TypeScript", sayHello() { },} : { name: string; sayHello(): void; }
69+
70+
name: "TypeScript",
71+
>name : string
72+
>"TypeScript" : "TypeScript"
73+
74+
sayHello() {
75+
>sayHello : () => void
76+
77+
},
78+
});
79+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Repro from #17041
2+
3+
interface Named {
4+
name: string;
5+
}
6+
7+
function parrot<T extends Named>(obj: T): T {
8+
return obj;
9+
}
10+
11+
12+
parrot({
13+
name: "TypeScript",
14+
});
15+
16+
parrot({
17+
name: "TypeScript",
18+
age: 5,
19+
});
20+
21+
parrot({
22+
name: "TypeScript",
23+
age: function () { },
24+
});
25+
26+
parrot({
27+
name: "TypeScript",
28+
sayHello() {
29+
},
30+
});

0 commit comments

Comments
 (0)