Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contextually type IIFE params by their arguments #8483

Merged
merged 3 commits into from
May 6, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Member

@ahejlsberg ahejlsberg May 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a rest parameter I think you need to collect all of the remaining arguments starting at that position and compute a union of their types. For example:

((...a) => {
    // Type of a should be (number | string)[]
})(1, 2, "hello");

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return createArrayType(type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not get a union type of all the remaining arguments and use that instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mhegazy Guess we both had that idea 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
return type;
}
}
const contextualSignature = getContextualSignature(func);
if (contextualSignature) {
const funcHasRestParameters = hasRestParameter(func);
Expand All @@ -8540,6 +8551,13 @@ namespace ts {
return undefined;
}

function isIife(func: FunctionExpression | MethodDeclaration) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call it isIIFE or isImmediatelyInvokedFunctionExpression.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) &&
func.parent.kind === SyntaxKind.ParenthesizedExpression &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessarily. this is a valid iife (function f() {)} ()).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using the skipParenthesizedNodes function before checking for a call expression at the top.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, although skipParenthesizedNodes actually goes down, not up (expression = expression.expression), so I just wrote an inline loop to go up (parent = parent.parent)

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,
Expand Down Expand Up @@ -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
Expand Down
90 changes: 90 additions & 0 deletions tests/baselines/reference/contextuallyTypedIife.js
Original file line number Diff line number Diff line change
@@ -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;
})();
97 changes: 97 additions & 0 deletions tests/baselines/reference/contextuallyTypedIife.symbols
Original file line number Diff line number Diff line change
@@ -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))



Loading