From e239f86d3faced2dcd88f9b35f24f88327421cfb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 11 Jun 2018 14:01:16 -0700 Subject: [PATCH] Flatten spread expressions of tuple types --- src/compiler/checker.ts | 24 ++++++++++++++++++++++-- src/compiler/types.ts | 6 ++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 468ac0c8c20..5a3bca1896c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18097,7 +18097,25 @@ namespace ts { return node.attributes.properties.length > 0 ? [node.attributes] : emptyArray; } else { - return node.arguments || emptyArray; + const args = node.arguments || emptyArray; + const length = args.length; + if (length && args[length - 1].kind === SyntaxKind.SpreadElement && getSpreadArgumentIndex(args) === length - 1) { + // We have a spread argument in the last position and no other spread arguments. If the type + // of the argument is a tuple type, spread the tuple elements into the argument list. We can + // call checkExpressionCached because spread expressions never have a contextual type. + const spreadArgument = args[length - 1]; + const type = checkExpressionCached(spreadArgument.expression); + if (isTupleType(type)) { + const syntheticArgs = map((type).typeArguments || emptyArray, t => { + const arg = createNode(SyntaxKind.SyntheticExpression, spreadArgument.pos, spreadArgument.end); + arg.parent = spreadArgument; + arg.type = t; + return arg; + }); + return concatenate(args.slice(0, length - 1), syntheticArgs); + } + } + return args; } } @@ -21015,6 +21033,8 @@ namespace ts { return undefinedWideningType; case SyntaxKind.YieldExpression: return checkYieldExpression(node); + case SyntaxKind.SyntheticExpression: + return (node).type; case SyntaxKind.JsxExpression: return checkJsxExpression(node, checkMode); case SyntaxKind.JsxElement: @@ -21059,7 +21079,7 @@ namespace ts { } function isRestParameterType(type: Type) { - return isArrayType(type) || isTupleType(type) || type.flags & TypeFlags.TypeParameter && isArrayType(getBaseConstraintOfType(type) || unknownType); + return isArrayType(type) || isTupleType(type) || type.flags & TypeFlags.Instantiable && isTypeAssignableTo(type, anyArrayType); } function checkParameter(node: ParameterDeclaration) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ff30d4e3442..da8b44ccbe8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -320,6 +320,7 @@ namespace ts { AsExpression, NonNullExpression, MetaProperty, + SyntheticExpression, // Misc TemplateSpan, @@ -1338,6 +1339,11 @@ namespace ts { expression?: Expression; } + export interface SyntheticExpression extends Expression { + kind: SyntaxKind.SyntheticExpression; + type: Type; + } + // see: https://tc39.github.io/ecma262/#prod-ExponentiationExpression export type ExponentiationOperator = SyntaxKind.AsteriskAsteriskToken