Skip to content

Commit 3139cf2

Browse files
joeywattsrbuckton
authored andcommitted
Move class property transformation into new transformer. (#30467)
Signed-off-by: Joseph Watts <[email protected]>
1 parent e6fde9e commit 3139cf2

11 files changed

+807
-434
lines changed

src/compiler/binder.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,8 +3229,7 @@ namespace ts {
32293229
// A ClassDeclaration is ES6 syntax.
32303230
transformFlags = subtreeFlags | TransformFlags.AssertES2015;
32313231

3232-
// A class with a parameter property assignment, property initializer, computed property name, or decorator is
3233-
// TypeScript syntax.
3232+
// A class with a parameter property assignment or decorator is TypeScript syntax.
32343233
// An exported declaration may be TypeScript syntax, but is handled by the visitor
32353234
// for a namespace declaration.
32363235
if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax)
@@ -3247,8 +3246,7 @@ namespace ts {
32473246
// A ClassExpression is ES6 syntax.
32483247
let transformFlags = subtreeFlags | TransformFlags.AssertES2015;
32493248

3250-
// A class with a parameter property assignment, property initializer, or decorator is
3251-
// TypeScript syntax.
3249+
// A class with a parameter property assignment or decorator is TypeScript syntax.
32523250
if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax
32533251
|| node.typeParameters) {
32543252
transformFlags |= TransformFlags.AssertTypeScript;
@@ -3338,7 +3336,6 @@ namespace ts {
33383336
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
33393337
|| node.typeParameters
33403338
|| node.type
3341-
|| (node.name && isComputedPropertyName(node.name)) // While computed method names aren't typescript, the TS transform must visit them to emit property declarations correctly
33423339
|| !node.body) {
33433340
transformFlags |= TransformFlags.AssertTypeScript;
33443341
}
@@ -3369,7 +3366,6 @@ namespace ts {
33693366
if (node.decorators
33703367
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
33713368
|| node.type
3372-
|| (node.name && isComputedPropertyName(node.name)) // While computed accessor names aren't typescript, the TS transform must visit them to emit property declarations correctly
33733369
|| !node.body) {
33743370
transformFlags |= TransformFlags.AssertTypeScript;
33753371
}
@@ -3384,12 +3380,15 @@ namespace ts {
33843380
}
33853381

33863382
function computePropertyDeclaration(node: PropertyDeclaration, subtreeFlags: TransformFlags) {
3387-
// A PropertyDeclaration is TypeScript syntax.
3388-
let transformFlags = subtreeFlags | TransformFlags.AssertTypeScript;
3383+
let transformFlags = subtreeFlags | TransformFlags.ContainsClassFields;
3384+
3385+
// Decorators, TypeScript-specific modifiers, and type annotations are TypeScript syntax.
3386+
if (some(node.decorators) || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.type) {
3387+
transformFlags |= TransformFlags.AssertTypeScript;
3388+
}
33893389

3390-
// If the PropertyDeclaration has an initializer or a computed name, we need to inform its ancestor
3391-
// so that it handle the transformation.
3392-
if (node.initializer || isComputedPropertyName(node.name)) {
3390+
// Hoisted variables related to class properties should live within the TypeScript class wrapper.
3391+
if (isComputedPropertyName(node.name) || (hasStaticModifier(node) && node.initializer)) {
33933392
transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax;
33943393
}
33953394

src/compiler/transformer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ namespace ts {
4444
addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory));
4545

4646
transformers.push(transformTypeScript);
47+
transformers.push(transformClassFields);
4748

4849
if (jsx === JsxEmit.React) {
4950
transformers.push(transformJsx);

src/compiler/transformers/classFields.ts

Lines changed: 491 additions & 0 deletions
Large diffs are not rendered by default.

src/compiler/transformers/ts.ts

Lines changed: 146 additions & 347 deletions
Large diffs are not rendered by default.

src/compiler/transformers/utilities.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,47 @@ namespace ts {
240240
isIdentifier(expression);
241241
}
242242

243+
/**
244+
* A simple inlinable expression is an expression which can be copied into multiple locations
245+
* without risk of repeating any sideeffects and whose value could not possibly change between
246+
* any such locations
247+
*/
248+
export function isSimpleInlineableExpression(expression: Expression) {
249+
return !isIdentifier(expression) && isSimpleCopiableExpression(expression) ||
250+
isWellKnownSymbolSyntactically(expression);
251+
}
252+
253+
/**
254+
* Adds super call and preceding prologue directives into the list of statements.
255+
*
256+
* @param ctor The constructor node.
257+
* @param result The list of statements.
258+
* @param visitor The visitor to apply to each node added to the result array.
259+
* @returns index of the statement that follows super call
260+
*/
261+
export function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[], visitor: Visitor): number {
262+
if (ctor.body) {
263+
const statements = ctor.body.statements;
264+
// add prologue directives to the list (if any)
265+
const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor);
266+
if (index === statements.length) {
267+
// list contains nothing but prologue directives (or empty) - exit
268+
return index;
269+
}
270+
271+
const statement = statements[index];
272+
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
273+
result.push(visitNode(statement, visitor, isStatement));
274+
return index + 1;
275+
}
276+
277+
return index;
278+
}
279+
280+
return 0;
281+
}
282+
283+
243284
/**
244285
* @param input Template string input strings
245286
* @param args Names which need to be made file-level unique
@@ -255,4 +296,43 @@ namespace ts {
255296
return result;
256297
};
257298
}
299+
300+
/**
301+
* Gets all property declarations with initializers on either the static or instance side of a class.
302+
*
303+
* @param node The class node.
304+
* @param isStatic A value indicating whether to get properties from the static or instance side of the class.
305+
*/
306+
export function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray<PropertyDeclaration> {
307+
return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty);
308+
}
309+
310+
/**
311+
* Gets a value indicating whether a class element is a static property declaration with an initializer.
312+
*
313+
* @param member The class element node.
314+
*/
315+
export function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
316+
return isInitializedProperty(member) && hasStaticModifier(member);
317+
}
318+
319+
/**
320+
* Gets a value indicating whether a class element is an instance property declaration with an initializer.
321+
*
322+
* @param member The class element node.
323+
*/
324+
export function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
325+
return isInitializedProperty(member) && !hasStaticModifier(member);
326+
}
327+
328+
/**
329+
* Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer.
330+
*
331+
* @param member The class element node.
332+
* @param isStatic A value indicating whether the member should be a static or instance member.
333+
*/
334+
export function isInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } {
335+
return member.kind === SyntaxKind.PropertyDeclaration
336+
&& (<PropertyDeclaration>member).initializer !== undefined;
337+
}
258338
}

src/compiler/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"transformers/utilities.ts",
3131
"transformers/destructuring.ts",
3232
"transformers/ts.ts",
33+
"transformers/classFields.ts",
3334
"transformers/es2017.ts",
3435
"transformers/es2018.ts",
3536
"transformers/es2019.ts",

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5197,6 +5197,7 @@ namespace ts {
51975197
ContainsYield = 1 << 17,
51985198
ContainsHoistedDeclarationOrCompletion = 1 << 18,
51995199
ContainsDynamicImport = 1 << 19,
5200+
ContainsClassFields = 1 << 20,
52005201

52015202
// Please leave this as 1 << 29.
52025203
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.

tests/baselines/reference/declarationEmitPrivateSymbolCausesVarDeclarationEmit2.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ var C = /** @class */ (function () {
3434
}
3535
return C;
3636
}());
37-
_a = a_1.x;
3837
exports.C = C;
38+
_a = a_1.x;
3939
//// [c.js]
4040
"use strict";
4141
var __extends = (this && this.__extends) || (function () {
@@ -64,8 +64,8 @@ var D = /** @class */ (function (_super) {
6464
}
6565
return D;
6666
}(b_1.C));
67-
_a = a_1.x;
6867
exports.D = D;
68+
_a = a_1.x;
6969

7070

7171
//// [a.d.ts]

0 commit comments

Comments
 (0)