Skip to content

Commit 6ea8ceb

Browse files
committed
Transform param patterns/initializers after object rest
1 parent 4e39023 commit 6ea8ceb

File tree

3 files changed

+154
-19
lines changed

3 files changed

+154
-19
lines changed

src/compiler/transformers/es2018.ts

Lines changed: 121 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ namespace ts {
5959
let exportedVariableStatement = false;
6060
let enabledSubstitutions: ESNextSubstitutionFlags;
6161
let enclosingFunctionFlags: FunctionFlags;
62+
let parameterHasPrecedingObjectRestOrSpread: Set<ParameterDeclaration> | undefined;
6263
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
6364
let hierarchyFacts: HierarchyFacts = 0;
6465

@@ -785,7 +786,24 @@ namespace ts {
785786
);
786787
}
787788

789+
function parameterVisitor(node: Node) {
790+
Debug.assertNode(node, isParameter);
791+
return visitParameter(node);
792+
}
793+
788794
function visitParameter(node: ParameterDeclaration): ParameterDeclaration {
795+
if (parameterHasPrecedingObjectRestOrSpread?.has(node)) {
796+
return factory.updateParameterDeclaration(
797+
node,
798+
/*decorators*/ undefined,
799+
/*modifiers*/ undefined,
800+
node.dotDotDotToken,
801+
isBindingPattern(node.name) ? factory.getGeneratedNameForNode(node) : node.name,
802+
/*questionToken*/ undefined,
803+
/*type*/ undefined,
804+
/*initializer*/ undefined
805+
);
806+
}
789807
if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
790808
// Binding patterns are converted into a generated name and are
791809
// evaluated inside the function body.
@@ -803,54 +821,76 @@ namespace ts {
803821
return visitEachChild(node, visitor, context);
804822
}
805823

824+
function recordParameterHasPrecedingObjectRestOrSpread(node: SignatureDeclaration) {
825+
for (const parameter of node.parameters) {
826+
if (parameterHasPrecedingObjectRestOrSpread) {
827+
parameterHasPrecedingObjectRestOrSpread.add(parameter);
828+
}
829+
else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
830+
parameterHasPrecedingObjectRestOrSpread = new Set();
831+
}
832+
}
833+
}
834+
806835
function visitConstructorDeclaration(node: ConstructorDeclaration) {
807836
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
808-
enclosingFunctionFlags = FunctionFlags.Normal;
837+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
838+
enclosingFunctionFlags = getFunctionFlags(node);
839+
recordParameterHasPrecedingObjectRestOrSpread(node);
809840
const updated = factory.updateConstructorDeclaration(
810841
node,
811842
/*decorators*/ undefined,
812843
node.modifiers,
813-
visitParameterList(node.parameters, visitor, context),
844+
visitParameterList(node.parameters, parameterVisitor, context),
814845
transformFunctionBody(node)
815846
);
816847
enclosingFunctionFlags = savedEnclosingFunctionFlags;
848+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
817849
return updated;
818850
}
819851

820852
function visitGetAccessorDeclaration(node: GetAccessorDeclaration) {
821853
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
822-
enclosingFunctionFlags = FunctionFlags.Normal;
854+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
855+
enclosingFunctionFlags = getFunctionFlags(node);
856+
recordParameterHasPrecedingObjectRestOrSpread(node);
823857
const updated = factory.updateGetAccessorDeclaration(
824858
node,
825859
/*decorators*/ undefined,
826860
node.modifiers,
827861
visitNode(node.name, visitor, isPropertyName),
828-
visitParameterList(node.parameters, visitor, context),
862+
visitParameterList(node.parameters, parameterVisitor, context),
829863
/*type*/ undefined,
830864
transformFunctionBody(node)
831865
);
832866
enclosingFunctionFlags = savedEnclosingFunctionFlags;
867+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
833868
return updated;
834869
}
835870

836871
function visitSetAccessorDeclaration(node: SetAccessorDeclaration) {
837872
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
838-
enclosingFunctionFlags = FunctionFlags.Normal;
873+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
874+
enclosingFunctionFlags = getFunctionFlags(node);
875+
recordParameterHasPrecedingObjectRestOrSpread(node);
839876
const updated = factory.updateSetAccessorDeclaration(
840877
node,
841878
/*decorators*/ undefined,
842879
node.modifiers,
843880
visitNode(node.name, visitor, isPropertyName),
844-
visitParameterList(node.parameters, visitor, context),
881+
visitParameterList(node.parameters, parameterVisitor, context),
845882
transformFunctionBody(node)
846883
);
847884
enclosingFunctionFlags = savedEnclosingFunctionFlags;
885+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
848886
return updated;
849887
}
850888

851889
function visitMethodDeclaration(node: MethodDeclaration) {
852890
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
891+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
853892
enclosingFunctionFlags = getFunctionFlags(node);
893+
recordParameterHasPrecedingObjectRestOrSpread(node);
854894
const updated = factory.updateMethodDeclaration(
855895
node,
856896
/*decorators*/ undefined,
@@ -863,19 +903,22 @@ namespace ts {
863903
visitNode(node.name, visitor, isPropertyName),
864904
visitNode<Token<SyntaxKind.QuestionToken>>(/*questionToken*/ undefined, visitor, isToken),
865905
/*typeParameters*/ undefined,
866-
visitParameterList(node.parameters, visitor, context),
906+
visitParameterList(node.parameters, parameterVisitor, context),
867907
/*type*/ undefined,
868908
enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
869909
? transformAsyncGeneratorFunctionBody(node)
870910
: transformFunctionBody(node)
871911
);
872912
enclosingFunctionFlags = savedEnclosingFunctionFlags;
913+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
873914
return updated;
874915
}
875916

876917
function visitFunctionDeclaration(node: FunctionDeclaration) {
877918
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
919+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
878920
enclosingFunctionFlags = getFunctionFlags(node);
921+
recordParameterHasPrecedingObjectRestOrSpread(node);
879922
const updated = factory.updateFunctionDeclaration(
880923
node,
881924
/*decorators*/ undefined,
@@ -887,35 +930,41 @@ namespace ts {
887930
: node.asteriskToken,
888931
node.name,
889932
/*typeParameters*/ undefined,
890-
visitParameterList(node.parameters, visitor, context),
933+
visitParameterList(node.parameters, parameterVisitor, context),
891934
/*type*/ undefined,
892935
enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
893936
? transformAsyncGeneratorFunctionBody(node)
894937
: transformFunctionBody(node)
895938
);
896939
enclosingFunctionFlags = savedEnclosingFunctionFlags;
940+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
897941
return updated;
898942
}
899943

900944
function visitArrowFunction(node: ArrowFunction) {
901945
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
946+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
902947
enclosingFunctionFlags = getFunctionFlags(node);
948+
recordParameterHasPrecedingObjectRestOrSpread(node);
903949
const updated = factory.updateArrowFunction(
904950
node,
905951
node.modifiers,
906952
/*typeParameters*/ undefined,
907-
visitParameterList(node.parameters, visitor, context),
953+
visitParameterList(node.parameters, parameterVisitor, context),
908954
/*type*/ undefined,
909955
node.equalsGreaterThanToken,
910956
transformFunctionBody(node),
911957
);
912958
enclosingFunctionFlags = savedEnclosingFunctionFlags;
959+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
913960
return updated;
914961
}
915962

916963
function visitFunctionExpression(node: FunctionExpression) {
917964
const savedEnclosingFunctionFlags = enclosingFunctionFlags;
965+
const savedParameterHasPrecedingObjectRestOrSpread = parameterHasPrecedingObjectRestOrSpread;
918966
enclosingFunctionFlags = getFunctionFlags(node);
967+
recordParameterHasPrecedingObjectRestOrSpread(node);
919968
const updated = factory.updateFunctionExpression(
920969
node,
921970
enclosingFunctionFlags & FunctionFlags.Generator
@@ -926,13 +975,14 @@ namespace ts {
926975
: node.asteriskToken,
927976
node.name,
928977
/*typeParameters*/ undefined,
929-
visitParameterList(node.parameters, visitor, context),
978+
visitParameterList(node.parameters, parameterVisitor, context),
930979
/*type*/ undefined,
931980
enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
932981
? transformAsyncGeneratorFunctionBody(node)
933982
: transformFunctionBody(node)
934983
);
935984
enclosingFunctionFlags = savedEnclosingFunctionFlags;
985+
parameterHasPrecedingObjectRestOrSpread = savedParameterHasPrecedingObjectRestOrSpread;
936986
return updated;
937987
}
938988

@@ -1007,6 +1057,7 @@ namespace ts {
10071057
statementOffset = factory.copyPrologue(body.statements, statements, /*ensureUseStrict*/ false, visitor);
10081058
}
10091059
addRange(statements, appendObjectRestAssignmentsIfNeeded(/*statements*/ undefined, node));
1060+
10101061
const leadingStatements = endLexicalEnvironment();
10111062
if (statementOffset > 0 || some(statements) || some(leadingStatements)) {
10121063
const block = factory.converters.convertToFunctionBlock(body, /*multiLine*/ true);
@@ -1018,25 +1069,76 @@ namespace ts {
10181069
}
10191070

10201071
function appendObjectRestAssignmentsIfNeeded(statements: Statement[] | undefined, node: FunctionLikeDeclaration): Statement[] | undefined {
1072+
let containsPrecedingObjectRestOrSpread = false;
10211073
for (const parameter of node.parameters) {
1022-
if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
1023-
const temp = factory.getGeneratedNameForNode(parameter);
1074+
if (containsPrecedingObjectRestOrSpread) {
1075+
if (isBindingPattern(parameter.name)) {
1076+
// In cases where a binding pattern is simply '[]' or '{}',
1077+
// we usually don't want to emit a var declaration; however, in the presence
1078+
// of an initializer, we must emit that expression to preserve side effects.
1079+
//
1080+
// NOTE: see `insertDefaultValueAssignmentForBindingPattern` in es2015.ts
1081+
if (parameter.name.elements.length > 0) {
1082+
const declarations = flattenDestructuringBinding(
1083+
parameter,
1084+
visitor,
1085+
context,
1086+
FlattenLevel.All,
1087+
factory.getGeneratedNameForNode(parameter));
1088+
if (some(declarations)) {
1089+
const declarationList = factory.createVariableDeclarationList(declarations);
1090+
const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList);
1091+
setEmitFlags(statement, EmitFlags.CustomPrologue);
1092+
statements = append(statements, statement);
1093+
}
1094+
}
1095+
else if (parameter.initializer) {
1096+
const name = factory.getGeneratedNameForNode(parameter);
1097+
const initializer = visitNode(parameter.initializer, visitor, isExpression);
1098+
const assignment = factory.createAssignment(name, initializer);
1099+
const statement = factory.createExpressionStatement(assignment);
1100+
setEmitFlags(statement, EmitFlags.CustomPrologue);
1101+
statements = append(statements, statement);
1102+
}
1103+
}
1104+
else if (parameter.initializer) {
1105+
const name = factory.cloneNode(parameter.name);
1106+
setTextRange(name, parameter.name);
1107+
setEmitFlags(name, EmitFlags.NoSourceMap);
1108+
1109+
const initializer = visitNode(parameter.initializer, visitor, isExpression);
1110+
addEmitFlags(initializer, EmitFlags.NoSourceMap | EmitFlags.NoComments);
1111+
1112+
const assignment = factory.createAssignment(name, initializer);
1113+
setTextRange(assignment, parameter);
1114+
setEmitFlags(assignment, EmitFlags.NoComments);
1115+
1116+
const block = factory.createBlock([factory.createExpressionStatement(assignment)]);
1117+
setTextRange(block, parameter);
1118+
setEmitFlags(block, EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments);
1119+
1120+
const typeCheck = factory.createTypeCheck(factory.cloneNode(parameter.name), "undefined");
1121+
const statement = factory.createIfStatement(typeCheck, block);
1122+
startOnNewLine(statement);
1123+
setTextRange(statement, parameter);
1124+
setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue | EmitFlags.NoComments);
1125+
statements = append(statements, statement);
1126+
}
1127+
}
1128+
else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
1129+
containsPrecedingObjectRestOrSpread = true;
10241130
const declarations = flattenDestructuringBinding(
10251131
parameter,
10261132
visitor,
10271133
context,
10281134
FlattenLevel.ObjectRest,
1029-
temp,
1135+
factory.getGeneratedNameForNode(parameter),
10301136
/*doNotRecordTempVariablesInLine*/ false,
10311137
/*skipInitializer*/ true,
10321138
);
10331139
if (some(declarations)) {
1034-
const statement = factory.createVariableStatement(
1035-
/*modifiers*/ undefined,
1036-
factory.createVariableDeclarationList(
1037-
declarations
1038-
)
1039-
);
1140+
const declarationList = factory.createVariableDeclarationList(declarations);
1141+
const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList);
10401142
setEmitFlags(statement, EmitFlags.CustomPrologue);
10411143
statements = append(statements, statement);
10421144
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [functionParameterObjectRestAndInitializers.ts]
2+
// https://github.com/microsoft/TypeScript/issues/47079
3+
4+
function f({a, ...x}, b = a) {
5+
return b;
6+
}
7+
8+
9+
//// [functionParameterObjectRestAndInitializers.js]
10+
// https://github.com/microsoft/TypeScript/issues/47079
11+
var __rest = (this && this.__rest) || function (s, e) {
12+
var t = {};
13+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14+
t[p] = s[p];
15+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
16+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18+
t[p[i]] = s[p[i]];
19+
}
20+
return t;
21+
};
22+
function f(_a, b) {
23+
var { a } = _a, x = __rest(_a, ["a"]);
24+
if (b === void 0) { b = a; }
25+
return b;
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @target: es2015
2+
// @noTypesAndSymbols: true
3+
// https://github.com/microsoft/TypeScript/issues/47079
4+
5+
function f({a, ...x}, b = a) {
6+
return b;
7+
}

0 commit comments

Comments
 (0)