-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mhegazy Guess we both had that idea 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
@@ -8540,6 +8551,13 @@ namespace ts { | |
return undefined; | ||
} | ||
|
||
function isIife(func: FunctionExpression | MethodDeclaration) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would call it There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not necessarily. this is a valid iife There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, although skipParenthesizedNodes actually goes down, not up ( |
||
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 | ||
|
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; | ||
})(); |
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)) | ||
|
||
|
||
|
There was a problem hiding this comment.
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:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done