Skip to content

Commit c172314

Browse files
committed
Infer contextual tuple types
1 parent a1746d4 commit c172314

File tree

1 file changed

+38
-38
lines changed

1 file changed

+38
-38
lines changed

src/compiler/checker.ts

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12386,22 +12386,7 @@ namespace ts {
1238612386
callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i));
1238712387
}
1238812388
if (targetRestTypeVariable) {
12389-
const sourceRestTypeVariable = getRestTypeParameter(source);
12390-
if (sourceRestTypeVariable && paramCount === sourceCount - 1) {
12391-
callback(sourceRestTypeVariable, targetRestTypeVariable);
12392-
}
12393-
else {
12394-
const types = [];
12395-
const names = [];
12396-
for (let i = paramCount; i < maxCount; i++) {
12397-
types.push(getTypeAtPosition(source, i));
12398-
names.push(getParameterNameAtPosition(source, i));
12399-
}
12400-
const minArgumentCount = getMinArgumentCount(source);
12401-
const minLength = minArgumentCount < paramCount ? 0 : minArgumentCount - paramCount;
12402-
const rest = createTupleType(types, minLength, sourceHasRest, names);
12403-
callback(rest, targetRestTypeVariable);
12404-
}
12389+
callback(getRestTypeAtPosition(source, paramCount), targetRestTypeVariable);
1240512390
}
1240612391
}
1240712392

@@ -15203,43 +15188,35 @@ namespace ts {
1520315188
}
1520415189
const iife = getImmediatelyInvokedFunctionExpression(func);
1520515190
if (iife && iife.arguments) {
15191+
const args = getEffectiveCallArguments(iife)!;
1520615192
const indexOfParameter = func.parameters.indexOf(parameter);
1520715193
if (parameter.dotDotDotToken) {
15208-
const restTypes: Type[] = [];
15209-
for (let i = indexOfParameter; i < iife.arguments.length; i++) {
15210-
restTypes.push(getWidenedLiteralType(checkExpression(iife.arguments[i])));
15211-
}
15212-
return restTypes.length ? createArrayType(getUnionType(restTypes)) : undefined;
15194+
return getSpreadArgumentType(iife, args, indexOfParameter, args.length, anyType, /*context*/ undefined);
1521315195
}
1521415196
const links = getNodeLinks(iife);
1521515197
const cached = links.resolvedSignature;
1521615198
links.resolvedSignature = anySignature;
15217-
const type = indexOfParameter < iife.arguments.length ?
15218-
getWidenedLiteralType(checkExpression(iife.arguments[indexOfParameter])) :
15199+
const type = indexOfParameter < args.length ?
15200+
getWidenedLiteralType(checkExpression(args[indexOfParameter])) :
1521915201
parameter.initializer ? undefined : undefinedWideningType;
1522015202
links.resolvedSignature = cached;
1522115203
return type;
1522215204
}
1522315205
const contextualSignature = getContextualSignature(func);
1522415206
if (contextualSignature) {
15225-
const funcHasRestParameters = hasRestParameter(func);
15226-
const len = func.parameters.length - (funcHasRestParameters ? 1 : 0);
15207+
const funcHasRestParameter = hasRestParameter(func);
15208+
const len = func.parameters.length - (funcHasRestParameter ? 1 : 0);
1522715209
let indexOfParameter = func.parameters.indexOf(parameter);
1522815210
if (getThisParameter(func) !== undefined && !contextualSignature.thisParameter) {
1522915211
Debug.assert(indexOfParameter !== 0); // Otherwise we should not have called `getContextuallyTypedParameterType`.
1523015212
indexOfParameter -= 1;
1523115213
}
15232-
1523315214
if (indexOfParameter < len) {
1523415215
return getTypeAtPosition(contextualSignature, indexOfParameter);
1523515216
}
15236-
1523715217
// If last parameter is contextually rest parameter get its type
15238-
if (funcHasRestParameters &&
15239-
indexOfParameter === func.parameters.length - 1 &&
15240-
hasEffectiveRestParameter(contextualSignature) &&
15241-
func.parameters.length >= contextualSignature.parameters.length) {
15242-
return getTypeOfRestParameter(contextualSignature);
15218+
if (funcHasRestParameter && indexOfParameter === len) {
15219+
return getRestTypeAtPosition(contextualSignature, indexOfParameter);
1524315220
}
1524415221
}
1524515222
}
@@ -17806,7 +17783,7 @@ namespace ts {
1780617783
}
1780717784
}
1780817785

17809-
function isSpreadArgument(arg: Expression | undefined) {
17786+
function isSpreadArgument(arg: Expression | undefined): arg is Expression {
1781017787
return !!arg && (arg.kind === SyntaxKind.SpreadElement || arg.kind === SyntaxKind.SyntheticExpression && (<SyntheticExpression>arg).isSpread);
1781117788
}
1781217789

@@ -18028,12 +18005,14 @@ namespace ts {
1802818005
}
1802918006

1803018007
function getSpreadArgumentType(node: CallLikeExpression, args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
18031-
if (index === argCount - 1) {
18032-
const arg = getEffectiveArgument(node, args, index);
18008+
if (index >= argCount - 1) {
18009+
const arg = getEffectiveArgument(node, args, argCount - 1);
1803318010
if (isSpreadArgument(arg)) {
1803418011
// We are inferring from a spread expression in the last argument position, i.e. both the parameter
1803518012
// and the argument are ...x forms.
18036-
return checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context);
18013+
return arg.kind === SyntaxKind.SyntheticExpression ?
18014+
createArrayType((<SyntheticExpression>arg).type) :
18015+
checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context);
1803718016
}
1803818017
}
1803918018
const contextualType = getIndexTypeOfType(restType, IndexKind.Number) || anyType;
@@ -19560,6 +19539,27 @@ namespace ts {
1956019539
return anyType;
1956119540
}
1956219541

19542+
function getRestTypeAtPosition(source: Signature, pos: number): Type {
19543+
const paramCount = getParameterCount(source);
19544+
const hasRest = hasEffectiveRestParameter(source);
19545+
if (hasRest && pos === paramCount - 1) {
19546+
const restTypeVariable = getRestTypeParameter(source);
19547+
if (restTypeVariable) {
19548+
return restTypeVariable;
19549+
}
19550+
}
19551+
const start = hasRest ? Math.min(pos, paramCount - 1) : pos;
19552+
const types = [];
19553+
const names = [];
19554+
for (let i = start; i < paramCount; i++) {
19555+
types.push(getTypeAtPosition(source, i));
19556+
names.push(getParameterNameAtPosition(source, i));
19557+
}
19558+
const minArgumentCount = getMinArgumentCount(source);
19559+
const minLength = minArgumentCount < start ? 0 : minArgumentCount - start;
19560+
return createTupleType(types, minLength, hasRest, names);
19561+
}
19562+
1956319563
function getTypeOfRestParameter(signature: Signature) {
1956419564
if (signature.hasRestParameter) {
1956519565
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
@@ -19653,11 +19653,11 @@ namespace ts {
1965319653
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType);
1965419654
}
1965519655
}
19656-
if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) {
19656+
if (signature.hasRestParameter) {
1965719657
// parameter might be a transient symbol generated by use of `arguments` in the function body.
1965819658
const parameter = last(signature.parameters);
1965919659
if (isTransientSymbol(parameter) || !getEffectiveTypeAnnotationNode(<ParameterDeclaration>parameter.valueDeclaration)) {
19660-
const contextualParameterType = getTypeOfSymbol(last(context.parameters));
19660+
const contextualParameterType = getRestTypeAtPosition(context, len);
1966119661
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType);
1966219662
}
1966319663
}

0 commit comments

Comments
 (0)