From aa38cdba56eada4dd7f88dbf738df0d59177b25d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 5 May 2016 11:58:36 -0700 Subject: [PATCH 1/3] Contextually type IIFE params by their arguments --- src/compiler/checker.ts | 24 ++- .../reference/contextuallyTypedIife.js | 90 ++++++++ .../reference/contextuallyTypedIife.symbols | 97 +++++++++ .../reference/contextuallyTypedIife.types | 202 ++++++++++++++++++ .../functions/contextuallyTypedIife.ts | 26 +++ 5 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/contextuallyTypedIife.js create mode 100644 tests/baselines/reference/contextuallyTypedIife.symbols create mode 100644 tests/baselines/reference/contextuallyTypedIife.types create mode 100644 tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b03aa99e7d9..4ee4b17e4e5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8520,6 +8520,17 @@ namespace ts { function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { + if (isIife(func)) { + const indexOfParameter = indexOf(func.parameters, parameter); + const call = func.parent.parent as CallExpression; + if (indexOfParameter < call.arguments.length) { + const type = getTypeOfExpression(call.arguments[indexOfParameter]); + if (type && parameter.dotDotDotToken) { + return createArrayType(type); + } + return type; + } + } const contextualSignature = getContextualSignature(func); if (contextualSignature) { const funcHasRestParameters = hasRestParameter(func); @@ -8540,6 +8551,13 @@ namespace ts { return undefined; } + function isIife(func: FunctionExpression | MethodDeclaration) { + return (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) && + func.parent.kind === SyntaxKind.ParenthesizedExpression && + func.parent.parent.kind === SyntaxKind.CallExpression && + (func.parent.parent as CallExpression).expression === func.parent; + } + // In a variable, parameter or property declaration with a type annotation, // the contextual type of an initializer expression is the type of the variable, parameter or property. // Otherwise, in a parameter declaration of a contextually typed function expression, @@ -8898,9 +8916,9 @@ namespace ts { } function getContextualTypeForFunctionLikeDeclaration(node: FunctionExpression | MethodDeclaration) { - return isObjectLiteralMethod(node) - ? getContextualTypeForObjectLiteralMethod(node) - : getApparentTypeOfContextualType(node); + return isObjectLiteralMethod(node) ? + getContextualTypeForObjectLiteralMethod(node) : + getApparentTypeOfContextualType(node); } // Return the contextual signature for a given expression node. A contextual type provides a diff --git a/tests/baselines/reference/contextuallyTypedIife.js b/tests/baselines/reference/contextuallyTypedIife.js new file mode 100644 index 00000000000..544acea0a46 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedIife.js @@ -0,0 +1,90 @@ +//// [contextuallyTypedIife.ts] +// arrow +(jake => { })("build"); +// function expression +(function (cats) { })("lol"); +// multiple arguments +((a, b, c) => { })("foo", 101, false); +// contextually typed parameters. +(f => f(1))(i => i + 1); +// default parameters +((m = 10) => m + 1)(12); +((n = 10) => n + 1)(); +// optional parameters +((j?) => j + 1)(12); +((k?) => k + 1)(); +((l, o?) => l + o)(12); // o should be any +// rest parameters +((...numbers) => numbers.every(n => n > 0))(5,6,7); +((...noNumbers) => noNumbers.some(n => n > 0))(); +((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); +// destructuring parameters (with defaults too!) +(({ q }) => q)({ q : 13 }); +(({ p = 14 }) => p)({ p : 15 }); +(({ r = 17 } = { r: 18 }) => r)({r : 19}); +(({ u = 22 } = { u: 23 }) => u)(); + + + + +//// [contextuallyTypedIife.js] +// arrow +(function (jake) { })("build"); +// function expression +(function (cats) { })("lol"); +// multiple arguments +(function (a, b, c) { })("foo", 101, false); +// contextually typed parameters. +(function (f) { return f(1); })(function (i) { return i + 1; }); +// default parameters +(function (m) { + if (m === void 0) { m = 10; } + return m + 1; +})(12); +(function (n) { + if (n === void 0) { n = 10; } + return n + 1; +})(); +// optional parameters +(function (j) { return j + 1; })(12); +(function (k) { return k + 1; })(); +(function (l, o) { return l + o; })(12); // o should be any +// rest parameters +(function () { + var numbers = []; + for (var _i = 0; _i < arguments.length; _i++) { + numbers[_i - 0] = arguments[_i]; + } + return numbers.every(function (n) { return n > 0; }); +})(5, 6, 7); +(function () { + var noNumbers = []; + for (var _i = 0; _i < arguments.length; _i++) { + noNumbers[_i - 0] = arguments[_i]; + } + return noNumbers.some(function (n) { return n > 0; }); +})(); +(function (first) { + var rest = []; + for (var _i = 1; _i < arguments.length; _i++) { + rest[_i - 1] = arguments[_i]; + } + return first ? [] : rest.map(function (n) { return n > 0; }); +})(8, 9, 10); +// destructuring parameters (with defaults too!) +(function (_a) { + var q = _a.q; + return q; +})({ q: 13 }); +(function (_a) { + var _b = _a.p, p = _b === void 0 ? 14 : _b; + return p; +})({ p: 15 }); +(function (_a) { + var _b = (_a === void 0 ? { r: 18 } : _a).r, r = _b === void 0 ? 17 : _b; + return r; +})({ r: 19 }); +(function (_a) { + var _b = (_a === void 0 ? { u: 23 } : _a).u, u = _b === void 0 ? 22 : _b; + return u; +})(); diff --git a/tests/baselines/reference/contextuallyTypedIife.symbols b/tests/baselines/reference/contextuallyTypedIife.symbols new file mode 100644 index 00000000000..01001dc0f32 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedIife.symbols @@ -0,0 +1,97 @@ +=== tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts === +// arrow +(jake => { })("build"); +>jake : Symbol(jake, Decl(contextuallyTypedIife.ts, 1, 1)) + +// function expression +(function (cats) { })("lol"); +>cats : Symbol(cats, Decl(contextuallyTypedIife.ts, 3, 11)) + +// multiple arguments +((a, b, c) => { })("foo", 101, false); +>a : Symbol(a, Decl(contextuallyTypedIife.ts, 5, 2)) +>b : Symbol(b, Decl(contextuallyTypedIife.ts, 5, 4)) +>c : Symbol(c, Decl(contextuallyTypedIife.ts, 5, 7)) + +// contextually typed parameters. +(f => f(1))(i => i + 1); +>f : Symbol(f, Decl(contextuallyTypedIife.ts, 7, 1)) +>f : Symbol(f, Decl(contextuallyTypedIife.ts, 7, 1)) +>i : Symbol(i, Decl(contextuallyTypedIife.ts, 7, 12)) +>i : Symbol(i, Decl(contextuallyTypedIife.ts, 7, 12)) + +// default parameters +((m = 10) => m + 1)(12); +>m : Symbol(m, Decl(contextuallyTypedIife.ts, 9, 2)) +>m : Symbol(m, Decl(contextuallyTypedIife.ts, 9, 2)) + +((n = 10) => n + 1)(); +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 10, 2)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 10, 2)) + +// optional parameters +((j?) => j + 1)(12); +>j : Symbol(j, Decl(contextuallyTypedIife.ts, 12, 2)) +>j : Symbol(j, Decl(contextuallyTypedIife.ts, 12, 2)) + +((k?) => k + 1)(); +>k : Symbol(k, Decl(contextuallyTypedIife.ts, 13, 2)) +>k : Symbol(k, Decl(contextuallyTypedIife.ts, 13, 2)) + +((l, o?) => l + o)(12); // o should be any +>l : Symbol(l, Decl(contextuallyTypedIife.ts, 14, 2)) +>o : Symbol(o, Decl(contextuallyTypedIife.ts, 14, 4)) +>l : Symbol(l, Decl(contextuallyTypedIife.ts, 14, 2)) +>o : Symbol(o, Decl(contextuallyTypedIife.ts, 14, 4)) + +// rest parameters +((...numbers) => numbers.every(n => n > 0))(5,6,7); +>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 16, 2)) +>numbers.every : Symbol(Array.every, Decl(lib.d.ts, --, --)) +>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 16, 2)) +>every : Symbol(Array.every, Decl(lib.d.ts, --, --)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 16, 31)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 16, 31)) + +((...noNumbers) => noNumbers.some(n => n > 0))(); +>noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIife.ts, 17, 2)) +>noNumbers.some : Symbol(Array.some, Decl(lib.d.ts, --, --)) +>noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIife.ts, 17, 2)) +>some : Symbol(Array.some, Decl(lib.d.ts, --, --)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 34)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 34)) + +((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); +>first : Symbol(first, Decl(contextuallyTypedIife.ts, 18, 2)) +>rest : Symbol(rest, Decl(contextuallyTypedIife.ts, 18, 8)) +>first : Symbol(first, Decl(contextuallyTypedIife.ts, 18, 2)) +>rest.map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>rest : Symbol(rest, Decl(contextuallyTypedIife.ts, 18, 8)) +>map : Symbol(Array.map, Decl(lib.d.ts, --, --)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 43)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 43)) + +// destructuring parameters (with defaults too!) +(({ q }) => q)({ q : 13 }); +>q : Symbol(q, Decl(contextuallyTypedIife.ts, 20, 3)) +>q : Symbol(q, Decl(contextuallyTypedIife.ts, 20, 3)) +>q : Symbol(q, Decl(contextuallyTypedIife.ts, 20, 16)) + +(({ p = 14 }) => p)({ p : 15 }); +>p : Symbol(p, Decl(contextuallyTypedIife.ts, 21, 3)) +>p : Symbol(p, Decl(contextuallyTypedIife.ts, 21, 3)) +>p : Symbol(p, Decl(contextuallyTypedIife.ts, 21, 21)) + +(({ r = 17 } = { r: 18 }) => r)({r : 19}); +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 3)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 16)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 3)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 33)) + +(({ u = 22 } = { u: 23 }) => u)(); +>u : Symbol(u, Decl(contextuallyTypedIife.ts, 23, 3)) +>u : Symbol(u, Decl(contextuallyTypedIife.ts, 23, 16)) +>u : Symbol(u, Decl(contextuallyTypedIife.ts, 23, 3)) + + + diff --git a/tests/baselines/reference/contextuallyTypedIife.types b/tests/baselines/reference/contextuallyTypedIife.types new file mode 100644 index 00000000000..b2fc85dddfd --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedIife.types @@ -0,0 +1,202 @@ +=== tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts === +// arrow +(jake => { })("build"); +>(jake => { })("build") : void +>(jake => { }) : (jake: string) => void +>jake => { } : (jake: string) => void +>jake : string +>"build" : string + +// function expression +(function (cats) { })("lol"); +>(function (cats) { })("lol") : void +>(function (cats) { }) : (cats: string) => void +>function (cats) { } : (cats: string) => void +>cats : string +>"lol" : string + +// multiple arguments +((a, b, c) => { })("foo", 101, false); +>((a, b, c) => { })("foo", 101, false) : void +>((a, b, c) => { }) : (a: string, b: number, c: boolean) => void +>(a, b, c) => { } : (a: string, b: number, c: boolean) => void +>a : string +>b : number +>c : boolean +>"foo" : string +>101 : number +>false : boolean + +// contextually typed parameters. +(f => f(1))(i => i + 1); +>(f => f(1))(i => i + 1) : any +>(f => f(1)) : (f: (i: any) => any) => any +>f => f(1) : (f: (i: any) => any) => any +>f : (i: any) => any +>f(1) : any +>f : (i: any) => any +>1 : number +>i => i + 1 : (i: any) => any +>i : any +>i + 1 : any +>i : any +>1 : number + +// default parameters +((m = 10) => m + 1)(12); +>((m = 10) => m + 1)(12) : number +>((m = 10) => m + 1) : (m?: number) => number +>(m = 10) => m + 1 : (m?: number) => number +>m : number +>10 : number +>m + 1 : number +>m : number +>1 : number +>12 : number + +((n = 10) => n + 1)(); +>((n = 10) => n + 1)() : number +>((n = 10) => n + 1) : (n?: number) => number +>(n = 10) => n + 1 : (n?: number) => number +>n : number +>10 : number +>n + 1 : number +>n : number +>1 : number + +// optional parameters +((j?) => j + 1)(12); +>((j?) => j + 1)(12) : number +>((j?) => j + 1) : (j?: number) => number +>(j?) => j + 1 : (j?: number) => number +>j : number +>j + 1 : number +>j : number +>1 : number +>12 : number + +((k?) => k + 1)(); +>((k?) => k + 1)() : any +>((k?) => k + 1) : (k?: any) => any +>(k?) => k + 1 : (k?: any) => any +>k : any +>k + 1 : any +>k : any +>1 : number + +((l, o?) => l + o)(12); // o should be any +>((l, o?) => l + o)(12) : any +>((l, o?) => l + o) : (l: number, o?: any) => any +>(l, o?) => l + o : (l: number, o?: any) => any +>l : number +>o : any +>l + o : any +>l : number +>o : any +>12 : number + +// rest parameters +((...numbers) => numbers.every(n => n > 0))(5,6,7); +>((...numbers) => numbers.every(n => n > 0))(5,6,7) : boolean +>((...numbers) => numbers.every(n => n > 0)) : (...numbers: number[]) => boolean +>(...numbers) => numbers.every(n => n > 0) : (...numbers: number[]) => boolean +>numbers : number[] +>numbers.every(n => n > 0) : boolean +>numbers.every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean +>numbers : number[] +>every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean +>n => n > 0 : (n: number) => boolean +>n : number +>n > 0 : boolean +>n : number +>0 : number +>5 : number +>6 : number +>7 : number + +((...noNumbers) => noNumbers.some(n => n > 0))(); +>((...noNumbers) => noNumbers.some(n => n > 0))() : boolean +>((...noNumbers) => noNumbers.some(n => n > 0)) : (...noNumbers: any[]) => boolean +>(...noNumbers) => noNumbers.some(n => n > 0) : (...noNumbers: any[]) => boolean +>noNumbers : any[] +>noNumbers.some(n => n > 0) : boolean +>noNumbers.some : (callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any) => boolean +>noNumbers : any[] +>some : (callbackfn: (value: any, index: number, array: any[]) => boolean, thisArg?: any) => boolean +>n => n > 0 : (n: any) => boolean +>n : any +>n > 0 : boolean +>n : any +>0 : number + +((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); +>((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10) : boolean[] +>((first, ...rest) => first ? [] : rest.map(n => n > 0)) : (first: number, ...rest: number[]) => boolean[] +>(first, ...rest) => first ? [] : rest.map(n => n > 0) : (first: number, ...rest: number[]) => boolean[] +>first : number +>rest : number[] +>first ? [] : rest.map(n => n > 0) : boolean[] +>first : number +>[] : undefined[] +>rest.map(n => n > 0) : boolean[] +>rest.map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>rest : number[] +>map : (callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[] +>n => n > 0 : (n: number) => boolean +>n : number +>n > 0 : boolean +>n : number +>0 : number +>8 : number +>9 : number +>10 : number + +// destructuring parameters (with defaults too!) +(({ q }) => q)({ q : 13 }); +>(({ q }) => q)({ q : 13 }) : number +>(({ q }) => q) : ({q}: { q: number; }) => number +>({ q }) => q : ({q}: { q: number; }) => number +>q : number +>q : number +>{ q : 13 } : { q: number; } +>q : number +>13 : number + +(({ p = 14 }) => p)({ p : 15 }); +>(({ p = 14 }) => p)({ p : 15 }) : number +>(({ p = 14 }) => p) : ({p}: { p: number; }) => number +>({ p = 14 }) => p : ({p}: { p: number; }) => number +>p : number +>14 : number +>p : number +>{ p : 15 } : { p: number; } +>p : number +>15 : number + +(({ r = 17 } = { r: 18 }) => r)({r : 19}); +>(({ r = 17 } = { r: 18 }) => r)({r : 19}) : number +>(({ r = 17 } = { r: 18 }) => r) : ({r}?: { r: number; }) => number +>({ r = 17 } = { r: 18 }) => r : ({r}?: { r: number; }) => number +>r : number +>17 : number +>{ r: 18 } : { r: number; } +>r : number +>18 : number +>r : number +>{r : 19} : { r: number; } +>r : number +>19 : number + +(({ u = 22 } = { u: 23 }) => u)(); +>(({ u = 22 } = { u: 23 }) => u)() : number +>(({ u = 22 } = { u: 23 }) => u) : ({u}?: { u?: number; }) => number +>({ u = 22 } = { u: 23 }) => u : ({u}?: { u?: number; }) => number +>u : number +>22 : number +>{ u: 23 } : { u?: number; } +>u : number +>23 : number +>u : number + + + diff --git a/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts b/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts new file mode 100644 index 00000000000..169f5ffecf0 --- /dev/null +++ b/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts @@ -0,0 +1,26 @@ +// arrow +(jake => { })("build"); +// function expression +(function (cats) { })("lol"); +// multiple arguments +((a, b, c) => { })("foo", 101, false); +// contextually typed parameters. +(f => f(1))(i => i + 1); +// default parameters +((m = 10) => m + 1)(12); +((n = 10) => n + 1)(); +// optional parameters +((j?) => j + 1)(12); +((k?) => k + 1)(); +((l, o?) => l + o)(12); // o should be any +// rest parameters +((...numbers) => numbers.every(n => n > 0))(5,6,7); +((...noNumbers) => noNumbers.some(n => n > 0))(); +((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); +// destructuring parameters (with defaults too!) +(({ q }) => q)({ q : 13 }); +(({ p = 14 }) => p)({ p : 15 }); +(({ r = 17 } = { r: 18 }) => r)({r : 19}); +(({ u = 22 } = { u: 23 }) => u)(); + + From b0a387aafaafb97e04b17481dc12dbc9f84c9dc5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 5 May 2016 15:04:17 -0700 Subject: [PATCH 2/3] Address PR comments And address bug with contextually typed arguments that the PR changes exposed. --- src/compiler/checker.ts | 28 ++-- .../reference/contextuallyTypedIife.js | 26 +++- .../reference/contextuallyTypedIife.symbols | 122 +++++++++++------- .../reference/contextuallyTypedIife.types | 80 +++++++++--- .../functions/contextuallyTypedIife.ts | 11 +- 5 files changed, 181 insertions(+), 86 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4ee4b17e4e5..c3dff2371e7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8520,15 +8520,14 @@ namespace ts { function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - if (isIife(func)) { + const iife = getImmediatelyInvokedFunctionExpression(func); + if (iife) { const indexOfParameter = indexOf(func.parameters, parameter); - const call = func.parent.parent as CallExpression; - if (indexOfParameter < call.arguments.length) { - const type = getTypeOfExpression(call.arguments[indexOfParameter]); - if (type && parameter.dotDotDotToken) { - return createArrayType(type); + if (iife.arguments && indexOfParameter < iife.arguments.length) { + if (parameter.dotDotDotToken) { + return createArrayType(getUnionType(map(iife.arguments.slice(indexOfParameter), getTypeOfExpression))); } - return type; + return checkExpression(iife.arguments[indexOfParameter], identityMapper); } } const contextualSignature = getContextualSignature(func); @@ -8551,11 +8550,16 @@ namespace ts { return undefined; } - function isIife(func: FunctionExpression | MethodDeclaration) { - return (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) && - func.parent.kind === SyntaxKind.ParenthesizedExpression && - func.parent.parent.kind === SyntaxKind.CallExpression && - (func.parent.parent as CallExpression).expression === func.parent; + function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) { + if (isFunctionExpressionOrArrowFunction(func)) { + let parent = func.parent; + while (parent.kind === SyntaxKind.ParenthesizedExpression) { + parent = parent.parent; + } + if (parent.kind === SyntaxKind.CallExpression) { + return parent as CallExpression; + } + } } // In a variable, parameter or property declaration with a type annotation, diff --git a/tests/baselines/reference/contextuallyTypedIife.js b/tests/baselines/reference/contextuallyTypedIife.js index 544acea0a46..0a84e45e30b 100644 --- a/tests/baselines/reference/contextuallyTypedIife.js +++ b/tests/baselines/reference/contextuallyTypedIife.js @@ -3,10 +3,11 @@ (jake => { })("build"); // function expression (function (cats) { })("lol"); +// Lots of Irritating Superfluous Parentheses +(function (x) { } ("!")); +((((function (y) { }))))("-"); // multiple arguments ((a, b, c) => { })("foo", 101, false); -// contextually typed parameters. -(f => f(1))(i => i + 1); // default parameters ((m = 10) => m + 1)(12); ((n = 10) => n + 1)(); @@ -16,6 +17,7 @@ ((l, o?) => l + o)(12); // o should be any // rest parameters ((...numbers) => numbers.every(n => n > 0))(5,6,7); +((...mixed) => mixed.every(n => !!n))(5,'oops','oh no'); ((...noNumbers) => noNumbers.some(n => n > 0))(); ((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); // destructuring parameters (with defaults too!) @@ -23,8 +25,9 @@ (({ p = 14 }) => p)({ p : 15 }); (({ r = 17 } = { r: 18 }) => r)({r : 19}); (({ u = 22 } = { u: 23 }) => u)(); - - +// contextually typed parameters. +let twelve = (f => f(12))(i => i); +let eleven = (o => o.a(11))({ a: function(n) { return n; } }); //// [contextuallyTypedIife.js] @@ -32,10 +35,11 @@ (function (jake) { })("build"); // function expression (function (cats) { })("lol"); +// Lots of Irritating Superfluous Parentheses +(function (x) { }("!")); +((((function (y) { }))))("-"); // multiple arguments (function (a, b, c) { })("foo", 101, false); -// contextually typed parameters. -(function (f) { return f(1); })(function (i) { return i + 1; }); // default parameters (function (m) { if (m === void 0) { m = 10; } @@ -57,6 +61,13 @@ } return numbers.every(function (n) { return n > 0; }); })(5, 6, 7); +(function () { + var mixed = []; + for (var _i = 0; _i < arguments.length; _i++) { + mixed[_i - 0] = arguments[_i]; + } + return mixed.every(function (n) { return !!n; }); +})(5, 'oops', 'oh no'); (function () { var noNumbers = []; for (var _i = 0; _i < arguments.length; _i++) { @@ -88,3 +99,6 @@ var _b = (_a === void 0 ? { u: 23 } : _a).u, u = _b === void 0 ? 22 : _b; return u; })(); +// contextually typed parameters. +var twelve = (function (f) { return f(12); })(function (i) { return i; }); +var eleven = (function (o) { return o.a(11); })({ a: function (n) { return n; } }); diff --git a/tests/baselines/reference/contextuallyTypedIife.symbols b/tests/baselines/reference/contextuallyTypedIife.symbols index 01001dc0f32..824ce6031ce 100644 --- a/tests/baselines/reference/contextuallyTypedIife.symbols +++ b/tests/baselines/reference/contextuallyTypedIife.symbols @@ -7,91 +7,115 @@ (function (cats) { })("lol"); >cats : Symbol(cats, Decl(contextuallyTypedIife.ts, 3, 11)) +// Lots of Irritating Superfluous Parentheses +(function (x) { } ("!")); +>x : Symbol(x, Decl(contextuallyTypedIife.ts, 5, 11)) + +((((function (y) { }))))("-"); +>y : Symbol(y, Decl(contextuallyTypedIife.ts, 6, 14)) + // multiple arguments ((a, b, c) => { })("foo", 101, false); ->a : Symbol(a, Decl(contextuallyTypedIife.ts, 5, 2)) ->b : Symbol(b, Decl(contextuallyTypedIife.ts, 5, 4)) ->c : Symbol(c, Decl(contextuallyTypedIife.ts, 5, 7)) - -// contextually typed parameters. -(f => f(1))(i => i + 1); ->f : Symbol(f, Decl(contextuallyTypedIife.ts, 7, 1)) ->f : Symbol(f, Decl(contextuallyTypedIife.ts, 7, 1)) ->i : Symbol(i, Decl(contextuallyTypedIife.ts, 7, 12)) ->i : Symbol(i, Decl(contextuallyTypedIife.ts, 7, 12)) +>a : Symbol(a, Decl(contextuallyTypedIife.ts, 8, 2)) +>b : Symbol(b, Decl(contextuallyTypedIife.ts, 8, 4)) +>c : Symbol(c, Decl(contextuallyTypedIife.ts, 8, 7)) // default parameters ((m = 10) => m + 1)(12); ->m : Symbol(m, Decl(contextuallyTypedIife.ts, 9, 2)) ->m : Symbol(m, Decl(contextuallyTypedIife.ts, 9, 2)) +>m : Symbol(m, Decl(contextuallyTypedIife.ts, 10, 2)) +>m : Symbol(m, Decl(contextuallyTypedIife.ts, 10, 2)) ((n = 10) => n + 1)(); ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 10, 2)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 10, 2)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 11, 2)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 11, 2)) // optional parameters ((j?) => j + 1)(12); ->j : Symbol(j, Decl(contextuallyTypedIife.ts, 12, 2)) ->j : Symbol(j, Decl(contextuallyTypedIife.ts, 12, 2)) +>j : Symbol(j, Decl(contextuallyTypedIife.ts, 13, 2)) +>j : Symbol(j, Decl(contextuallyTypedIife.ts, 13, 2)) ((k?) => k + 1)(); ->k : Symbol(k, Decl(contextuallyTypedIife.ts, 13, 2)) ->k : Symbol(k, Decl(contextuallyTypedIife.ts, 13, 2)) +>k : Symbol(k, Decl(contextuallyTypedIife.ts, 14, 2)) +>k : Symbol(k, Decl(contextuallyTypedIife.ts, 14, 2)) ((l, o?) => l + o)(12); // o should be any ->l : Symbol(l, Decl(contextuallyTypedIife.ts, 14, 2)) ->o : Symbol(o, Decl(contextuallyTypedIife.ts, 14, 4)) ->l : Symbol(l, Decl(contextuallyTypedIife.ts, 14, 2)) ->o : Symbol(o, Decl(contextuallyTypedIife.ts, 14, 4)) +>l : Symbol(l, Decl(contextuallyTypedIife.ts, 15, 2)) +>o : Symbol(o, Decl(contextuallyTypedIife.ts, 15, 4)) +>l : Symbol(l, Decl(contextuallyTypedIife.ts, 15, 2)) +>o : Symbol(o, Decl(contextuallyTypedIife.ts, 15, 4)) // rest parameters ((...numbers) => numbers.every(n => n > 0))(5,6,7); ->numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 16, 2)) +>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 17, 2)) >numbers.every : Symbol(Array.every, Decl(lib.d.ts, --, --)) ->numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 16, 2)) +>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 17, 2)) >every : Symbol(Array.every, Decl(lib.d.ts, --, --)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 16, 31)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 16, 31)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 31)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 31)) + +((...mixed) => mixed.every(n => !!n))(5,'oops','oh no'); +>mixed : Symbol(mixed, Decl(contextuallyTypedIife.ts, 18, 2)) +>mixed.every : Symbol(Array.every, Decl(lib.d.ts, --, --)) +>mixed : Symbol(mixed, Decl(contextuallyTypedIife.ts, 18, 2)) +>every : Symbol(Array.every, Decl(lib.d.ts, --, --)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 27)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 27)) ((...noNumbers) => noNumbers.some(n => n > 0))(); ->noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIife.ts, 17, 2)) +>noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIife.ts, 19, 2)) >noNumbers.some : Symbol(Array.some, Decl(lib.d.ts, --, --)) ->noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIife.ts, 17, 2)) +>noNumbers : Symbol(noNumbers, Decl(contextuallyTypedIife.ts, 19, 2)) >some : Symbol(Array.some, Decl(lib.d.ts, --, --)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 34)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 34)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 19, 34)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 19, 34)) ((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); ->first : Symbol(first, Decl(contextuallyTypedIife.ts, 18, 2)) ->rest : Symbol(rest, Decl(contextuallyTypedIife.ts, 18, 8)) ->first : Symbol(first, Decl(contextuallyTypedIife.ts, 18, 2)) +>first : Symbol(first, Decl(contextuallyTypedIife.ts, 20, 2)) +>rest : Symbol(rest, Decl(contextuallyTypedIife.ts, 20, 8)) +>first : Symbol(first, Decl(contextuallyTypedIife.ts, 20, 2)) >rest.map : Symbol(Array.map, Decl(lib.d.ts, --, --)) ->rest : Symbol(rest, Decl(contextuallyTypedIife.ts, 18, 8)) +>rest : Symbol(rest, Decl(contextuallyTypedIife.ts, 20, 8)) >map : Symbol(Array.map, Decl(lib.d.ts, --, --)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 43)) ->n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 43)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 20, 43)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 20, 43)) // destructuring parameters (with defaults too!) (({ q }) => q)({ q : 13 }); ->q : Symbol(q, Decl(contextuallyTypedIife.ts, 20, 3)) ->q : Symbol(q, Decl(contextuallyTypedIife.ts, 20, 3)) ->q : Symbol(q, Decl(contextuallyTypedIife.ts, 20, 16)) +>q : Symbol(q, Decl(contextuallyTypedIife.ts, 22, 3)) +>q : Symbol(q, Decl(contextuallyTypedIife.ts, 22, 3)) +>q : Symbol(q, Decl(contextuallyTypedIife.ts, 22, 16)) (({ p = 14 }) => p)({ p : 15 }); ->p : Symbol(p, Decl(contextuallyTypedIife.ts, 21, 3)) ->p : Symbol(p, Decl(contextuallyTypedIife.ts, 21, 3)) ->p : Symbol(p, Decl(contextuallyTypedIife.ts, 21, 21)) +>p : Symbol(p, Decl(contextuallyTypedIife.ts, 23, 3)) +>p : Symbol(p, Decl(contextuallyTypedIife.ts, 23, 3)) +>p : Symbol(p, Decl(contextuallyTypedIife.ts, 23, 21)) (({ r = 17 } = { r: 18 }) => r)({r : 19}); ->r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 3)) ->r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 16)) ->r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 3)) ->r : Symbol(r, Decl(contextuallyTypedIife.ts, 22, 33)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 24, 3)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 24, 16)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 24, 3)) +>r : Symbol(r, Decl(contextuallyTypedIife.ts, 24, 33)) (({ u = 22 } = { u: 23 }) => u)(); ->u : Symbol(u, Decl(contextuallyTypedIife.ts, 23, 3)) ->u : Symbol(u, Decl(contextuallyTypedIife.ts, 23, 16)) ->u : Symbol(u, Decl(contextuallyTypedIife.ts, 23, 3)) +>u : Symbol(u, Decl(contextuallyTypedIife.ts, 25, 3)) +>u : Symbol(u, Decl(contextuallyTypedIife.ts, 25, 16)) +>u : Symbol(u, Decl(contextuallyTypedIife.ts, 25, 3)) +// contextually typed parameters. +let twelve = (f => f(12))(i => i); +>twelve : Symbol(twelve, Decl(contextuallyTypedIife.ts, 27, 3)) +>f : Symbol(f, Decl(contextuallyTypedIife.ts, 27, 14)) +>f : Symbol(f, Decl(contextuallyTypedIife.ts, 27, 14)) +>i : Symbol(i, Decl(contextuallyTypedIife.ts, 27, 26)) +>i : Symbol(i, Decl(contextuallyTypedIife.ts, 27, 26)) +let eleven = (o => o.a(11))({ a: function(n) { return n; } }); +>eleven : Symbol(eleven, Decl(contextuallyTypedIife.ts, 28, 3)) +>o : Symbol(o, Decl(contextuallyTypedIife.ts, 28, 14)) +>o.a : Symbol(a, Decl(contextuallyTypedIife.ts, 28, 29)) +>o : Symbol(o, Decl(contextuallyTypedIife.ts, 28, 14)) +>a : Symbol(a, Decl(contextuallyTypedIife.ts, 28, 29)) +>a : Symbol(a, Decl(contextuallyTypedIife.ts, 28, 29)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 28, 42)) +>n : Symbol(n, Decl(contextuallyTypedIife.ts, 28, 42)) diff --git a/tests/baselines/reference/contextuallyTypedIife.types b/tests/baselines/reference/contextuallyTypedIife.types index b2fc85dddfd..b673927e174 100644 --- a/tests/baselines/reference/contextuallyTypedIife.types +++ b/tests/baselines/reference/contextuallyTypedIife.types @@ -15,6 +15,24 @@ >cats : string >"lol" : string +// Lots of Irritating Superfluous Parentheses +(function (x) { } ("!")); +>(function (x) { } ("!")) : void +>function (x) { } ("!") : void +>function (x) { } : (x: string) => void +>x : string +>"!" : string + +((((function (y) { }))))("-"); +>((((function (y) { }))))("-") : void +>((((function (y) { })))) : (y: string) => void +>(((function (y) { }))) : (y: string) => void +>((function (y) { })) : (y: string) => void +>(function (y) { }) : (y: string) => void +>function (y) { } : (y: string) => void +>y : string +>"-" : string + // multiple arguments ((a, b, c) => { })("foo", 101, false); >((a, b, c) => { })("foo", 101, false) : void @@ -27,21 +45,6 @@ >101 : number >false : boolean -// contextually typed parameters. -(f => f(1))(i => i + 1); ->(f => f(1))(i => i + 1) : any ->(f => f(1)) : (f: (i: any) => any) => any ->f => f(1) : (f: (i: any) => any) => any ->f : (i: any) => any ->f(1) : any ->f : (i: any) => any ->1 : number ->i => i + 1 : (i: any) => any ->i : any ->i + 1 : any ->i : any ->1 : number - // default parameters ((m = 10) => m + 1)(12); >((m = 10) => m + 1)(12) : number @@ -114,6 +117,24 @@ >6 : number >7 : number +((...mixed) => mixed.every(n => !!n))(5,'oops','oh no'); +>((...mixed) => mixed.every(n => !!n))(5,'oops','oh no') : boolean +>((...mixed) => mixed.every(n => !!n)) : (...mixed: (number | string)[]) => boolean +>(...mixed) => mixed.every(n => !!n) : (...mixed: (number | string)[]) => boolean +>mixed : (number | string)[] +>mixed.every(n => !!n) : boolean +>mixed.every : (callbackfn: (value: number | string, index: number, array: (number | string)[]) => boolean, thisArg?: any) => boolean +>mixed : (number | string)[] +>every : (callbackfn: (value: number | string, index: number, array: (number | string)[]) => boolean, thisArg?: any) => boolean +>n => !!n : (n: number | string) => boolean +>n : number | string +>!!n : boolean +>!n : boolean +>n : number | string +>5 : number +>'oops' : string +>'oh no' : string + ((...noNumbers) => noNumbers.some(n => n > 0))(); >((...noNumbers) => noNumbers.some(n => n > 0))() : boolean >((...noNumbers) => noNumbers.some(n => n > 0)) : (...noNumbers: any[]) => boolean @@ -198,5 +219,34 @@ >23 : number >u : number +// contextually typed parameters. +let twelve = (f => f(12))(i => i); +>twelve : any +>(f => f(12))(i => i) : any +>(f => f(12)) : (f: {}) => any +>f => f(12) : (f: {}) => any +>f : {} +>f(12) : any +>f : {} +>12 : number +>i => i : (i: {}) => {} +>i : {} +>i : {} +let eleven = (o => o.a(11))({ a: function(n) { return n; } }); +>eleven : any +>(o => o.a(11))({ a: function(n) { return n; } }) : any +>(o => o.a(11)) : (o: { a: {}; }) => any +>o => o.a(11) : (o: { a: {}; }) => any +>o : { a: {}; } +>o.a(11) : any +>o.a : {} +>o : { a: {}; } +>a : {} +>11 : number +>{ a: function(n) { return n; } } : { a: (n: any) => any; } +>a : (n: any) => any +>function(n) { return n; } : (n: any) => any +>n : any +>n : any diff --git a/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts b/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts index 169f5ffecf0..ed3c24e0ac7 100644 --- a/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts +++ b/tests/cases/conformance/expressions/functions/contextuallyTypedIife.ts @@ -2,10 +2,11 @@ (jake => { })("build"); // function expression (function (cats) { })("lol"); +// Lots of Irritating Superfluous Parentheses +(function (x) { } ("!")); +((((function (y) { }))))("-"); // multiple arguments ((a, b, c) => { })("foo", 101, false); -// contextually typed parameters. -(f => f(1))(i => i + 1); // default parameters ((m = 10) => m + 1)(12); ((n = 10) => n + 1)(); @@ -15,6 +16,7 @@ ((l, o?) => l + o)(12); // o should be any // rest parameters ((...numbers) => numbers.every(n => n > 0))(5,6,7); +((...mixed) => mixed.every(n => !!n))(5,'oops','oh no'); ((...noNumbers) => noNumbers.some(n => n > 0))(); ((first, ...rest) => first ? [] : rest.map(n => n > 0))(8,9,10); // destructuring parameters (with defaults too!) @@ -22,5 +24,6 @@ (({ p = 14 }) => p)({ p : 15 }); (({ r = 17 } = { r: 18 }) => r)({r : 19}); (({ u = 22 } = { u: 23 }) => u)(); - - +// contextually typed parameters. +let twelve = (f => f(12))(i => i); +let eleven = (o => o.a(11))({ a: function(n) { return n; } }); From 96d3db212e274f31f2ec36f5d082ebadf910b348 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 5 May 2016 18:00:12 -0700 Subject: [PATCH 3/3] Only contextually type expressions of calls Also allocate once instead of twice. --- src/compiler/checker.ts | 14 +++++++--- .../reference/contextuallyTypedIife.types | 26 +++++++++---------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c3dff2371e7..0f392efce19 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8525,9 +8525,13 @@ namespace ts { const indexOfParameter = indexOf(func.parameters, parameter); if (iife.arguments && indexOfParameter < iife.arguments.length) { if (parameter.dotDotDotToken) { - return createArrayType(getUnionType(map(iife.arguments.slice(indexOfParameter), getTypeOfExpression))); + const restTypes: Type[] = []; + for (let i = indexOfParameter; i < iife.arguments.length; i++) { + restTypes.push(getTypeOfExpression(iife.arguments[i])); + } + return createArrayType(getUnionType(restTypes)); } - return checkExpression(iife.arguments[indexOfParameter], identityMapper); + return checkExpression(iife.arguments[indexOfParameter]); } } const contextualSignature = getContextualSignature(func); @@ -8552,11 +8556,13 @@ namespace ts { function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) { if (isFunctionExpressionOrArrowFunction(func)) { - let parent = func.parent; + let prev: Node = func; + let parent: Node = func.parent; while (parent.kind === SyntaxKind.ParenthesizedExpression) { + prev = parent; parent = parent.parent; } - if (parent.kind === SyntaxKind.CallExpression) { + if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) { return parent as CallExpression; } } diff --git a/tests/baselines/reference/contextuallyTypedIife.types b/tests/baselines/reference/contextuallyTypedIife.types index b673927e174..0f2092ef7c3 100644 --- a/tests/baselines/reference/contextuallyTypedIife.types +++ b/tests/baselines/reference/contextuallyTypedIife.types @@ -223,26 +223,26 @@ let twelve = (f => f(12))(i => i); >twelve : any >(f => f(12))(i => i) : any ->(f => f(12)) : (f: {}) => any ->f => f(12) : (f: {}) => any ->f : {} +>(f => f(12)) : (f: (i: any) => any) => any +>f => f(12) : (f: (i: any) => any) => any +>f : (i: any) => any >f(12) : any ->f : {} +>f : (i: any) => any >12 : number ->i => i : (i: {}) => {} ->i : {} ->i : {} +>i => i : (i: any) => any +>i : any +>i : any let eleven = (o => o.a(11))({ a: function(n) { return n; } }); >eleven : any >(o => o.a(11))({ a: function(n) { return n; } }) : any ->(o => o.a(11)) : (o: { a: {}; }) => any ->o => o.a(11) : (o: { a: {}; }) => any ->o : { a: {}; } +>(o => o.a(11)) : (o: { a: (n: any) => any; }) => any +>o => o.a(11) : (o: { a: (n: any) => any; }) => any +>o : { a: (n: any) => any; } >o.a(11) : any ->o.a : {} ->o : { a: {}; } ->a : {} +>o.a : (n: any) => any +>o : { a: (n: any) => any; } +>a : (n: any) => any >11 : number >{ a: function(n) { return n; } } : { a: (n: any) => any; } >a : (n: any) => any