Skip to content

Commit ecb2115

Browse files
authored
Merge pull request #13358 from Microsoft/iifeMissingArguments
Infer optional types for IIFE parameters with missing arguments
2 parents 73fbe5a + d0aa306 commit ecb2115

10 files changed

+611
-1033
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5058,9 +5058,10 @@ namespace ts {
50585058
if (!links.resolvedSignature) {
50595059
const parameters: Symbol[] = [];
50605060
let hasLiteralTypes = false;
5061-
let minArgumentCount = -1;
5061+
let minArgumentCount = 0;
50625062
let thisParameter: Symbol = undefined;
50635063
let hasThisParameter: boolean;
5064+
const iife = getImmediatelyInvokedFunctionExpression(declaration);
50645065
const isJSConstructSignature = isJSDocConstructSignature(declaration);
50655066

50665067
// If this is a JSDoc construct signature, then skip the first parameter in the
@@ -5087,14 +5088,12 @@ namespace ts {
50875088
hasLiteralTypes = true;
50885089
}
50895090

5090-
if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) {
5091-
if (minArgumentCount < 0) {
5092-
minArgumentCount = i - (hasThisParameter ? 1 : 0);
5093-
}
5094-
}
5095-
else {
5096-
// If we see any required parameters, it means the prior ones were not in fact optional.
5097-
minArgumentCount = -1;
5091+
// Record a new minimum argument count if this is not an optional parameter
5092+
const isOptionalParameter = param.initializer || param.questionToken || param.dotDotDotToken ||
5093+
iife && parameters.length > iife.arguments.length && !param.type ||
5094+
isJSDocOptionalParameter(param);
5095+
if (!isOptionalParameter) {
5096+
minArgumentCount = parameters.length;
50985097
}
50995098
}
51005099

@@ -5109,13 +5108,6 @@ namespace ts {
51095108
}
51105109
}
51115110

5112-
if (minArgumentCount < 0) {
5113-
minArgumentCount = declaration.parameters.length - (hasThisParameter ? 1 : 0);
5114-
}
5115-
if (isJSConstructSignature) {
5116-
minArgumentCount--;
5117-
}
5118-
51195111
const classType = declaration.kind === SyntaxKind.Constructor ?
51205112
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol))
51215113
: undefined;
@@ -10935,23 +10927,23 @@ namespace ts {
1093510927
const func = parameter.parent;
1093610928
if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
1093710929
const iife = getImmediatelyInvokedFunctionExpression(func);
10938-
if (iife) {
10930+
if (iife && iife.arguments) {
1093910931
const indexOfParameter = indexOf(func.parameters, parameter);
10940-
if (iife.arguments && indexOfParameter < iife.arguments.length) {
10941-
if (parameter.dotDotDotToken) {
10942-
const restTypes: Type[] = [];
10943-
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
10944-
restTypes.push(getWidenedLiteralType(checkExpression(iife.arguments[i])));
10945-
}
10946-
return createArrayType(getUnionType(restTypes));
10947-
}
10948-
const links = getNodeLinks(iife);
10949-
const cached = links.resolvedSignature;
10950-
links.resolvedSignature = anySignature;
10951-
const type = getWidenedLiteralType(checkExpression(iife.arguments[indexOfParameter]));
10952-
links.resolvedSignature = cached;
10953-
return type;
10954-
}
10932+
if (parameter.dotDotDotToken) {
10933+
const restTypes: Type[] = [];
10934+
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
10935+
restTypes.push(getWidenedLiteralType(checkExpression(iife.arguments[i])));
10936+
}
10937+
return restTypes.length ? createArrayType(getUnionType(restTypes)) : undefined;
10938+
}
10939+
const links = getNodeLinks(iife);
10940+
const cached = links.resolvedSignature;
10941+
links.resolvedSignature = anySignature;
10942+
const type = indexOfParameter < iife.arguments.length ?
10943+
getWidenedLiteralType(checkExpression(iife.arguments[indexOfParameter])) :
10944+
parameter.initializer ? undefined : undefinedWideningType;
10945+
links.resolvedSignature = cached;
10946+
return type;
1095510947
}
1095610948
const contextualSignature = getContextualSignature(func);
1095710949
if (contextualSignature) {

tests/baselines/reference/contextuallyTypedIife.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
// contextually typed parameters.
2929
let twelve = (f => f(12))(i => i);
3030
let eleven = (o => o.a(11))({ a: function(n) { return n; } });
31+
// missing arguments
32+
(function(x, undefined) { return x; })(42);
33+
((x, y, z) => 42)();
3134

3235

3336
//// [contextuallyTypedIife.js]
@@ -102,3 +105,6 @@ let eleven = (o => o.a(11))({ a: function(n) { return n; } });
102105
// contextually typed parameters.
103106
var twelve = (function (f) { return f(12); })(function (i) { return i; });
104107
var eleven = (function (o) { return o.a(11); })({ a: function (n) { return n; } });
108+
// missing arguments
109+
(function (x, undefined) { return x; })(42);
110+
(function (x, y, z) { return 42; })();

tests/baselines/reference/contextuallyTypedIife.symbols

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,14 @@ let eleven = (o => o.a(11))({ a: function(n) { return n; } });
119119
>n : Symbol(n, Decl(contextuallyTypedIife.ts, 28, 42))
120120
>n : Symbol(n, Decl(contextuallyTypedIife.ts, 28, 42))
121121

122+
// missing arguments
123+
(function(x, undefined) { return x; })(42);
124+
>x : Symbol(x, Decl(contextuallyTypedIife.ts, 30, 10))
125+
>undefined : Symbol(undefined, Decl(contextuallyTypedIife.ts, 30, 12))
126+
>x : Symbol(x, Decl(contextuallyTypedIife.ts, 30, 10))
127+
128+
((x, y, z) => 42)();
129+
>x : Symbol(x, Decl(contextuallyTypedIife.ts, 31, 2))
130+
>y : Symbol(y, Decl(contextuallyTypedIife.ts, 31, 4))
131+
>z : Symbol(z, Decl(contextuallyTypedIife.ts, 31, 7))
132+

tests/baselines/reference/contextuallyTypedIife.types

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,22 @@ let eleven = (o => o.a(11))({ a: function(n) { return n; } });
250250
>n : any
251251
>n : any
252252

253+
// missing arguments
254+
(function(x, undefined) { return x; })(42);
255+
>(function(x, undefined) { return x; })(42) : number
256+
>(function(x, undefined) { return x; }) : (x: number, undefined: any) => number
257+
>function(x, undefined) { return x; } : (x: number, undefined: any) => number
258+
>x : number
259+
>undefined : any
260+
>x : number
261+
>42 : 42
262+
263+
((x, y, z) => 42)();
264+
>((x, y, z) => 42)() : number
265+
>((x, y, z) => 42) : (x: any, y: any, z: any) => number
266+
>(x, y, z) => 42 : (x: any, y: any, z: any) => number
267+
>x : any
268+
>y : any
269+
>z : any
270+
>42 : 42
271+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//// [contextuallyTypedIifeStrict.ts]
2+
// arrow
3+
(jake => { })("build");
4+
// function expression
5+
(function (cats) { })("lol");
6+
// Lots of Irritating Superfluous Parentheses
7+
(function (x) { } ("!"));
8+
((((function (y) { }))))("-");
9+
// multiple arguments
10+
((a, b, c) => { })("foo", 101, false);
11+
// default parameters
12+
((m = 10) => m + 1)(12);
13+
((n = 10) => n + 1)();
14+
// optional parameters
15+
((j?) => j + 1)(12);
16+
((k?) => k + 1)();
17+
((l, o?) => l + o)(12); // o should be any
18+
// rest parameters
19+
((...numbers) => numbers.every(n => n > 0))(5,6,7);
20+
((...mixed) => mixed.every(n => !!n))(5,'oops','oh no');
21+
((...noNumbers) => noNumbers.some(n => n > 0))();
22+
((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10);
23+
// destructuring parameters (with defaults too!)
24+
(({ q }) => q)({ q : 13 });
25+
(({ p = 14 }) => p)({ p : 15 });
26+
(({ r = 17 } = { r: 18 }) => r)({r : 19});
27+
(({ u = 22 } = { u: 23 }) => u)();
28+
// contextually typed parameters.
29+
let twelve = (f => f(12))(i => i);
30+
let eleven = (o => o.a(11))({ a: function(n) { return n; } });
31+
// missing arguments
32+
(function(x, undefined) { return x; })(42);
33+
((x, y, z) => 42)();
34+
35+
36+
//// [contextuallyTypedIifeStrict.js]
37+
// arrow
38+
(function (jake) { })("build");
39+
// function expression
40+
(function (cats) { })("lol");
41+
// Lots of Irritating Superfluous Parentheses
42+
(function (x) { }("!"));
43+
((((function (y) { }))))("-");
44+
// multiple arguments
45+
(function (a, b, c) { })("foo", 101, false);
46+
// default parameters
47+
(function (m) {
48+
if (m === void 0) { m = 10; }
49+
return m + 1;
50+
})(12);
51+
(function (n) {
52+
if (n === void 0) { n = 10; }
53+
return n + 1;
54+
})();
55+
// optional parameters
56+
(function (j) { return j + 1; })(12);
57+
(function (k) { return k + 1; })();
58+
(function (l, o) { return l + o; })(12); // o should be any
59+
// rest parameters
60+
(function () {
61+
var numbers = [];
62+
for (var _i = 0; _i < arguments.length; _i++) {
63+
numbers[_i] = arguments[_i];
64+
}
65+
return numbers.every(function (n) { return n > 0; });
66+
})(5, 6, 7);
67+
(function () {
68+
var mixed = [];
69+
for (var _i = 0; _i < arguments.length; _i++) {
70+
mixed[_i] = arguments[_i];
71+
}
72+
return mixed.every(function (n) { return !!n; });
73+
})(5, 'oops', 'oh no');
74+
(function () {
75+
var noNumbers = [];
76+
for (var _i = 0; _i < arguments.length; _i++) {
77+
noNumbers[_i] = arguments[_i];
78+
}
79+
return noNumbers.some(function (n) { return n > 0; });
80+
})();
81+
(function (first) {
82+
var rest = [];
83+
for (var _i = 1; _i < arguments.length; _i++) {
84+
rest[_i - 1] = arguments[_i];
85+
}
86+
return first ? [] : rest.map(function (n) { return n > 0; });
87+
})(8, 9, 10);
88+
// destructuring parameters (with defaults too!)
89+
(function (_a) {
90+
var q = _a.q;
91+
return q;
92+
})({ q: 13 });
93+
(function (_a) {
94+
var _b = _a.p, p = _b === void 0 ? 14 : _b;
95+
return p;
96+
})({ p: 15 });
97+
(function (_a) {
98+
var _b = (_a === void 0 ? { r: 18 } : _a).r, r = _b === void 0 ? 17 : _b;
99+
return r;
100+
})({ r: 19 });
101+
(function (_a) {
102+
var _b = (_a === void 0 ? { u: 23 } : _a).u, u = _b === void 0 ? 22 : _b;
103+
return u;
104+
})();
105+
// contextually typed parameters.
106+
var twelve = (function (f) { return f(12); })(function (i) { return i; });
107+
var eleven = (function (o) { return o.a(11); })({ a: function (n) { return n; } });
108+
// missing arguments
109+
(function (x, undefined) { return x; })(42);
110+
(function (x, y, z) { return 42; })();
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
=== tests/cases/conformance/expressions/functions/contextuallyTypedIifeStrict.ts ===
2+
// arrow
3+
(jake => { })("build");
4+
>jake : Symbol(jake, Decl(contextuallyTypedIifeStrict.ts, 1, 1))
5+
6+
// function expression
7+
(function (cats) { })("lol");
8+
>cats : Symbol(cats, Decl(contextuallyTypedIifeStrict.ts, 3, 11))
9+
10+
// Lots of Irritating Superfluous Parentheses
11+
(function (x) { } ("!"));
12+
>x : Symbol(x, Decl(contextuallyTypedIifeStrict.ts, 5, 11))
13+
14+
((((function (y) { }))))("-");
15+
>y : Symbol(y, Decl(contextuallyTypedIifeStrict.ts, 6, 14))
16+
17+
// multiple arguments
18+
((a, b, c) => { })("foo", 101, false);
19+
>a : Symbol(a, Decl(contextuallyTypedIifeStrict.ts, 8, 2))
20+
>b : Symbol(b, Decl(contextuallyTypedIifeStrict.ts, 8, 4))
21+
>c : Symbol(c, Decl(contextuallyTypedIifeStrict.ts, 8, 7))
22+
23+
// default parameters
24+
((m = 10) => m + 1)(12);
25+
>m : Symbol(m, Decl(contextuallyTypedIifeStrict.ts, 10, 2))
26+
>m : Symbol(m, Decl(contextuallyTypedIifeStrict.ts, 10, 2))
27+
28+
((n = 10) => n + 1)();
29+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 11, 2))
30+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 11, 2))
31+
32+
// optional parameters
33+
((j?) => j + 1)(12);
34+
>j : Symbol(j, Decl(contextuallyTypedIifeStrict.ts, 13, 2))
35+
>j : Symbol(j, Decl(contextuallyTypedIifeStrict.ts, 13, 2))
36+
37+
((k?) => k + 1)();
38+
>k : Symbol(k, Decl(contextuallyTypedIifeStrict.ts, 14, 2))
39+
>k : Symbol(k, Decl(contextuallyTypedIifeStrict.ts, 14, 2))
40+
41+
((l, o?) => l + o)(12); // o should be any
42+
>l : Symbol(l, Decl(contextuallyTypedIifeStrict.ts, 15, 2))
43+
>o : Symbol(o, Decl(contextuallyTypedIifeStrict.ts, 15, 4))
44+
>l : Symbol(l, Decl(contextuallyTypedIifeStrict.ts, 15, 2))
45+
>o : Symbol(o, Decl(contextuallyTypedIifeStrict.ts, 15, 4))
46+
47+
// rest parameters
48+
((...numbers) => numbers.every(n => n > 0))(5,6,7);
49+
>numbers : Symbol(numbers, Decl(contextuallyTypedIifeStrict.ts, 17, 2))
50+
>numbers.every : Symbol(Array.every, Decl(lib.d.ts, --, --))
51+
>numbers : Symbol(numbers, Decl(contextuallyTypedIifeStrict.ts, 17, 2))
52+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
53+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 17, 31))
54+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 17, 31))
55+
56+
((...mixed) => mixed.every(n => !!n))(5,'oops','oh no');
57+
>mixed : Symbol(mixed, Decl(contextuallyTypedIifeStrict.ts, 18, 2))
58+
>mixed.every : Symbol(Array.every, Decl(lib.d.ts, --, --))
59+
>mixed : Symbol(mixed, Decl(contextuallyTypedIifeStrict.ts, 18, 2))
60+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
61+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 18, 27))
62+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 18, 27))
63+
64+
((...noNumbers) => noNumbers.some(n => n > 0))();
65+
>noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIifeStrict.ts, 19, 2))
66+
>noNumbers.some : Symbol(Array.some, Decl(lib.d.ts, --, --))
67+
>noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIifeStrict.ts, 19, 2))
68+
>some : Symbol(Array.some, Decl(lib.d.ts, --, --))
69+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 19, 34))
70+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 19, 34))
71+
72+
((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10);
73+
>first : Symbol(first, Decl(contextuallyTypedIifeStrict.ts, 20, 2))
74+
>rest : Symbol(rest, Decl(contextuallyTypedIifeStrict.ts, 20, 8))
75+
>first : Symbol(first, Decl(contextuallyTypedIifeStrict.ts, 20, 2))
76+
>rest.map : Symbol(Array.map, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
77+
>rest : Symbol(rest, Decl(contextuallyTypedIifeStrict.ts, 20, 8))
78+
>map : Symbol(Array.map, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
79+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 20, 43))
80+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 20, 43))
81+
82+
// destructuring parameters (with defaults too!)
83+
(({ q }) => q)({ q : 13 });
84+
>q : Symbol(q, Decl(contextuallyTypedIifeStrict.ts, 22, 3))
85+
>q : Symbol(q, Decl(contextuallyTypedIifeStrict.ts, 22, 3))
86+
>q : Symbol(q, Decl(contextuallyTypedIifeStrict.ts, 22, 16))
87+
88+
(({ p = 14 }) => p)({ p : 15 });
89+
>p : Symbol(p, Decl(contextuallyTypedIifeStrict.ts, 23, 3))
90+
>p : Symbol(p, Decl(contextuallyTypedIifeStrict.ts, 23, 3))
91+
>p : Symbol(p, Decl(contextuallyTypedIifeStrict.ts, 23, 21))
92+
93+
(({ r = 17 } = { r: 18 }) => r)({r : 19});
94+
>r : Symbol(r, Decl(contextuallyTypedIifeStrict.ts, 24, 3))
95+
>r : Symbol(r, Decl(contextuallyTypedIifeStrict.ts, 24, 16))
96+
>r : Symbol(r, Decl(contextuallyTypedIifeStrict.ts, 24, 3))
97+
>r : Symbol(r, Decl(contextuallyTypedIifeStrict.ts, 24, 33))
98+
99+
(({ u = 22 } = { u: 23 }) => u)();
100+
>u : Symbol(u, Decl(contextuallyTypedIifeStrict.ts, 25, 3))
101+
>u : Symbol(u, Decl(contextuallyTypedIifeStrict.ts, 25, 16))
102+
>u : Symbol(u, Decl(contextuallyTypedIifeStrict.ts, 25, 3))
103+
104+
// contextually typed parameters.
105+
let twelve = (f => f(12))(i => i);
106+
>twelve : Symbol(twelve, Decl(contextuallyTypedIifeStrict.ts, 27, 3))
107+
>f : Symbol(f, Decl(contextuallyTypedIifeStrict.ts, 27, 14))
108+
>f : Symbol(f, Decl(contextuallyTypedIifeStrict.ts, 27, 14))
109+
>i : Symbol(i, Decl(contextuallyTypedIifeStrict.ts, 27, 26))
110+
>i : Symbol(i, Decl(contextuallyTypedIifeStrict.ts, 27, 26))
111+
112+
let eleven = (o => o.a(11))({ a: function(n) { return n; } });
113+
>eleven : Symbol(eleven, Decl(contextuallyTypedIifeStrict.ts, 28, 3))
114+
>o : Symbol(o, Decl(contextuallyTypedIifeStrict.ts, 28, 14))
115+
>o.a : Symbol(a, Decl(contextuallyTypedIifeStrict.ts, 28, 29))
116+
>o : Symbol(o, Decl(contextuallyTypedIifeStrict.ts, 28, 14))
117+
>a : Symbol(a, Decl(contextuallyTypedIifeStrict.ts, 28, 29))
118+
>a : Symbol(a, Decl(contextuallyTypedIifeStrict.ts, 28, 29))
119+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 28, 42))
120+
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 28, 42))
121+
122+
// missing arguments
123+
(function(x, undefined) { return x; })(42);
124+
>x : Symbol(x, Decl(contextuallyTypedIifeStrict.ts, 30, 10))
125+
>undefined : Symbol(undefined, Decl(contextuallyTypedIifeStrict.ts, 30, 12))
126+
>x : Symbol(x, Decl(contextuallyTypedIifeStrict.ts, 30, 10))
127+
128+
((x, y, z) => 42)();
129+
>x : Symbol(x, Decl(contextuallyTypedIifeStrict.ts, 31, 2))
130+
>y : Symbol(y, Decl(contextuallyTypedIifeStrict.ts, 31, 4))
131+
>z : Symbol(z, Decl(contextuallyTypedIifeStrict.ts, 31, 7))
132+

0 commit comments

Comments
 (0)