From c69e4e7aff4c46197dcc55bfbfc677c4dbf4142d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 11 Jun 2018 06:12:39 -0700 Subject: [PATCH] Expand rest parameter with tuple types when emitting signatures --- src/compiler/checker.ts | 40 ++++++++++++++++++++++++++++------------ src/compiler/types.ts | 5 +++-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 98c1f5c8a96..584728c7c6c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3627,7 +3627,7 @@ namespace ts { typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); } - const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor)); + const parameters = getExpandedParameters(signature).map(parameter => symbolToParameterDeclaration(parameter, context, kind === SyntaxKind.Constructor)); if (signature.thisParameter) { const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context); parameters.unshift(thisParameter); @@ -3696,7 +3696,7 @@ namespace ts { const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); const modifiers = !(context.flags & NodeBuilderFlags.OmitParameterModifiers) && preserveModifierFlags && parameterDeclaration && parameterDeclaration.modifiers ? parameterDeclaration.modifiers.map(getSynthesizedClone) : undefined; - const isRest = parameterDeclaration ? isRestParameter(parameterDeclaration) : (parameterSymbol as TransientSymbol).isRestParameter; + const isRest = parameterDeclaration && isRestParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.RestParameter; const dotDotDotToken = isRest ? createToken(SyntaxKind.DotDotDotToken) : undefined; const name = parameterDeclaration ? parameterDeclaration.name ? @@ -3705,7 +3705,8 @@ namespace ts { cloneBindingName(parameterDeclaration.name) : symbolName(parameterSymbol) : symbolName(parameterSymbol); - const questionToken = parameterDeclaration && isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; + const isOptional = parameterDeclaration && isOptionalParameter(parameterDeclaration) || getCheckFlags(parameterSymbol) & CheckFlags.OptionalParameter; + const questionToken = isOptional ? createToken(SyntaxKind.QuestionToken) : undefined; const parameterNode = createParameter( /*decorators*/ undefined, modifiers, @@ -6198,6 +6199,27 @@ namespace ts { /*resolvedTypePredicate*/ undefined, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes); } + function getExpandedParameters(sig: Signature): Symbol[] { + if (sig.hasRestParameter) { + const restIndex = sig.parameters.length - 1; + const restParameter = sig.parameters[restIndex]; + const restType = getTypeOfSymbol(restParameter); + if (isTupleType(restType)) { + const elementTypes = (restType).typeArguments || emptyArray; + const minLength = ((restType).target).minLength; + const restParams = map(elementTypes, (t, i) => { + const name = restParameter.escapedName + "_" + i as __String; + const checkFlags = i >= minLength ? CheckFlags.OptionalParameter : 0; + const symbol = createSymbol(SymbolFlags.FunctionScopedVariable, name, checkFlags); + symbol.type = t; + return symbol; + }); + return concatenate(sig.parameters.slice(0, restIndex), restParams); + } + } + return sig.parameters; + } + function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); @@ -7376,9 +7398,8 @@ namespace ts { const lastParamVariadicType = firstDefined(lastParamTags, p => p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined); - const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String); + const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String, CheckFlags.RestParameter); syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType; - syntheticArgsSymbol.isRestParameter = true; if (lastParamVariadicType) { // Replace the last parameter with a rest parameter. parameters.pop(); @@ -9716,7 +9737,7 @@ namespace ts { } // Keep the flags from the symbol we're instantiating. Mark that is instantiated, and // also transient so that we can just store data on it directly. - const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | (getCheckFlags(symbol) & CheckFlags.Late)); + const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter)); result.declarations = symbol.declarations; result.parent = symbol.parent; result.target = symbol; @@ -9727,11 +9748,6 @@ namespace ts { if (symbol.nameType) { result.nameType = symbol.nameType; } - if (isTransientSymbol(symbol)) { - if (symbol.isRestParameter) { - result.isRestParameter = symbol.isRestParameter; - } - } return result; } @@ -21034,7 +21050,7 @@ namespace ts { } function isRestParameterType(type: Type) { - return isArrayType(type) || type.flags & TypeFlags.TypeParameter && isArrayType(getConstraintOfType(type) || unknownType); + return isArrayType(type) || isTupleType(type) || type.flags & TypeFlags.TypeParameter && isArrayType(getBaseConstraintOfType(type) || unknownType); } function checkParameter(node: ParameterDeclaration) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e5f601ccc9d..ba88dec8ce5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3548,14 +3548,15 @@ namespace ts { ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s) ContainsStatic = 1 << 9, // Synthetic property with static constituent(s) Late = 1 << 10, // Late-bound symbol for a computed property with a dynamic name - ReverseMapped = 1 << 11, // property of reverse-inferred homomorphic mapped type. + ReverseMapped = 1 << 11, // Property of reverse-inferred homomorphic mapped type + OptionalParameter = 1 << 12, // Optional parameter + RestParameter = 1 << 13, // Rest parameter Synthetic = SyntheticProperty | SyntheticMethod } /* @internal */ export interface TransientSymbol extends Symbol, SymbolLinks { checkFlags: CheckFlags; - isRestParameter?: boolean; } /* @internal */