From 75af3b7e021a04565bc90a6aae13ce4bd410da78 Mon Sep 17 00:00:00 2001 From: Joseph Watts Date: Thu, 20 Sep 2018 04:14:56 -0400 Subject: [PATCH 1/2] Move class property transformation into ESNext transformer Signed-off-by: Joseph Watts --- src/compiler/binder.ts | 24 +- src/compiler/transformers/esnext.ts | 442 +++++++++++++++++++++- src/compiler/transformers/ts.ts | 493 ++++++++----------------- src/compiler/transformers/utilities.ts | 80 ++++ 4 files changed, 677 insertions(+), 362 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ea6a9d6849b8d..7ab5c1362d163 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3189,8 +3189,7 @@ namespace ts { // A ClassDeclaration is ES6 syntax. transformFlags = subtreeFlags | TransformFlags.AssertES2015; - // A class with a parameter property assignment, property initializer, computed property name, or decorator is - // TypeScript syntax. + // A class with a parameter property assignment or decorator is TypeScript syntax. // An exported declaration may be TypeScript syntax, but is handled by the visitor // for a namespace declaration. if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax) @@ -3213,8 +3212,7 @@ namespace ts { // A ClassExpression is ES6 syntax. let transformFlags = subtreeFlags | TransformFlags.AssertES2015; - // A class with a parameter property assignment, property initializer, or decorator is - // TypeScript syntax. + // A class with a parameter property assignment or decorator is TypeScript syntax. if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax || node.typeParameters) { transformFlags |= TransformFlags.AssertTypeScript; @@ -3310,7 +3308,6 @@ namespace ts { || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.typeParameters || node.type - || (node.name && isComputedPropertyName(node.name)) // While computed method names aren't typescript, the TS transform must visit them to emit property declarations correctly || !node.body) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -3341,7 +3338,6 @@ namespace ts { if (node.decorators || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.type - || (node.name && isComputedPropertyName(node.name)) // While computed accessor names aren't typescript, the TS transform must visit them to emit property declarations correctly || !node.body) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -3356,12 +3352,16 @@ namespace ts { } function computePropertyDeclaration(node: PropertyDeclaration, subtreeFlags: TransformFlags) { - // A PropertyDeclaration is TypeScript syntax. - let transformFlags = subtreeFlags | TransformFlags.AssertTypeScript; + // A PropertyDeclaration is ESnext syntax. + let transformFlags = subtreeFlags | TransformFlags.AssertESNext; - // If the PropertyDeclaration has an initializer or a computed name, we need to inform its ancestor - // so that it handle the transformation. - if (node.initializer || isComputedPropertyName(node.name)) { + // Decorators, TypeScript-specific modifiers, and type annotations are TypeScript syntax. + if (some(node.decorators) || hasModifier(node, ModifierFlags.TypeScriptModifier) || node.type) { + transformFlags |= TransformFlags.AssertTypeScript; + } + + // Hoisted variables related to class properties should live within the TypeScript class wrapper. + if (isComputedPropertyName(node.name) || (hasStaticModifier(node) && node.initializer)) { transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax; } @@ -3762,6 +3762,8 @@ namespace ts { break; case SyntaxKind.ComputedPropertyName: + // Computed property names are transformed by the ESNext transformer. + transformFlags |= TransformFlags.AssertESNext; // Even though computed property names are ES6, we don't treat them as such. // This is so that they can flow through PropertyName transforms unaffected. // Instead, we mark the container as ES6, so that it can properly handle the transform. diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index cd62cca2c9dfb..73a55f8aa041e 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -2,7 +2,12 @@ namespace ts { const enum ESNextSubstitutionFlags { /** Enables substitutions for async methods with `super` calls. */ - AsyncMethodsWithSuper = 1 << 0 + AsyncMethodsWithSuper = 1 << 0, + /** + * Enables substitutions for class expressions with static fields + * which have initializers that reference the class name. + */ + ClassAliases = 1 << 1, } export function transformESNext(context: TransformationContext) { @@ -33,6 +38,20 @@ namespace ts { /** A set of node IDs for generated super accessors. */ const substitutedSuperAccessors: boolean[] = []; + let classAliases: Identifier[]; + + /** + * Tracks what computed name expressions originating from elided names must be inlined + * at the next execution site, in document order + */ + let pendingExpressions: Expression[] | undefined; + + /** + * Tracks what computed name expression statements and static property initializers must be + * emitted at the next execution site, in document order (for decorated classes). + */ + let pendingStatements: Statement[] | undefined; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { @@ -117,11 +136,374 @@ namespace ts { hasSuperElementAccess = true; } return visitEachChild(node, visitor, context); + case SyntaxKind.ClassExpression: + return visitClassExpression(node as ClassExpression); + case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node as ClassDeclaration); + case SyntaxKind.PropertyDeclaration: + return visitPropertyDeclaration(node as PropertyDeclaration); + case SyntaxKind.VariableStatement: + return visitVariableStatement(node as VariableStatement); + case SyntaxKind.ComputedPropertyName: + return visitComputedPropertyName(node as ComputedPropertyName); default: return visitEachChild(node, visitor, context); } } + /** + * Specialized visitor that visits the immediate children of a class with ESNext syntax. + * + * @param node The node to visit. + */ + function classElementVisitor(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.Constructor: + // Constructors for classes using ESNext syntax (like class properties) + // are transformed in `visitClassDeclaration` or `visitClassExpression`. + // We elide them here. The default visitor checks the transformFlags to + // determine whether the node contains ESNext syntax, so it can skip over + // constructors. + return undefined; + + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.IndexSignature: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MethodDeclaration: + // Fallback to the default visit behavior. + return visitor(node); + + case SyntaxKind.SemicolonClassElement: + return node; + + default: + return Debug.failBadSyntaxKind(node); + } + } + /** + * If the name is a computed property, this function transforms it, then either returns an expression which caches the + * value of the result or the expression itself if the value is either unused or safe to inline into multiple locations + * @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator) + */ + function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean): Expression | undefined { + if (isComputedPropertyName(name)) { + const expression = visitNode(name.expression, visitor, isExpression); + const innerExpression = skipPartiallyEmittedExpressions(expression); + const inlinable = isSimpleInlineableExpression(innerExpression); + const alreadyTransformed = isAssignmentExpression(innerExpression) && isGeneratedIdentifier(innerExpression.left); + if (!alreadyTransformed && !inlinable && shouldHoist) { + const generatedName = getGeneratedNameForNode(name); + hoistVariableDeclaration(generatedName); + return createAssignment(generatedName, expression); + } + return (inlinable || isIdentifier(innerExpression)) ? undefined : expression; + } + } + + function visitComputedPropertyName(name: ComputedPropertyName) { + let node = visitEachChild(name, visitor, context); + if (some(pendingExpressions)) { + const expressions = pendingExpressions; + expressions.push(name.expression); + pendingExpressions = []; + node = updateComputedPropertyName( + node, + inlineExpressions(expressions) + ); + } + return node; + } + + function visitPropertyDeclaration(node: PropertyDeclaration) { + Debug.assert(!some(node.decorators)); + // Create a temporary variable to store a computed property name (if necessary). + // If it's not inlineable, then we emit an expression after the class which assigns + // the property name to the temporary variable. + const expr = getPropertyNameExpressionIfNeeded(node.name, !!node.initializer); + if (expr && !isSimpleInlineableExpression(expr)) { + (pendingExpressions || (pendingExpressions = [])).push(expr); + } + return undefined; + } + + function visitClassDeclaration(node: ClassDeclaration) { + const savedPendingExpressions = pendingExpressions; + pendingExpressions = undefined; + + const extendsClauseElement = getEffectiveBaseTypeNode(node); + const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword); + + const statements: Statement[] = [ + updateClassDeclaration( + node, + node.decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + transformClassMembers(node, isDerivedClass) + ) + ]; + + // Write any pending expressions from elided or moved computed property names + if (some(pendingExpressions)) { + statements.push(createExpressionStatement(inlineExpressions(pendingExpressions!))); + } + pendingExpressions = savedPendingExpressions; + + // Emit static property assignment. Because classDeclaration is lexically evaluated, + // it is safe to emit static property assignment after classDeclaration + // From ES6 specification: + // HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using + // a lexical declaration such as a LexicalDeclaration or a ClassDeclaration. + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + if (some(staticProperties)) { + addInitializedPropertyStatements(statements, staticProperties, getInternalName(node)); + } + + return statements; + } + + function visitClassExpression(node: ClassExpression): Expression { + const savedPendingExpressions = pendingExpressions; + pendingExpressions = undefined; + + // If this class expression is a transformation of a decorated class declaration, + // then we want to output the pendingExpressions as statements, not as inlined + // expressions with the class statement. + // + // In this case, we use pendingStatements to produce the same output as the + // class declaration transformation. The VariableStatement visitor will insert + // these statements after the class expression variable statement. + const isDecoratedClassDeclaration = isClassDeclaration(getOriginalNode(node)); + + const staticProperties = getInitializedProperties(node, /*isStatic*/ true); + const extendsClauseElement = getEffectiveBaseTypeNode(node); + const isDerivedClass = !!(extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword); + + const classExpression = updateClassExpression( + node, + node.modifiers, + node.name, + node.typeParameters, + visitNodes(node.heritageClauses, visitor, isHeritageClause), + transformClassMembers(node, isDerivedClass) + ); + + if (some(staticProperties) || some(pendingExpressions)) { + if (isDecoratedClassDeclaration) { + Debug.assertDefined(pendingStatements, "Decorated classes transformed by TypeScript are expected to be within a variable declaration."); + + // Write any pending expressions from elided or moved computed property names + if (some(pendingExpressions)) { + pendingStatements!.push(createExpressionStatement(inlineExpressions(pendingExpressions!))); + } + pendingExpressions = savedPendingExpressions; + + if (some(staticProperties)) { + addInitializedPropertyStatements(pendingStatements!, staticProperties, getInternalName(node)); + } + return classExpression; + } + else { + const expressions: Expression[] = []; + const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference; + const temp = createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference); + if (isClassWithConstructorReference) { + // record an alias as the class name is not in scope for statics. + enableSubstitutionForClassAliases(); + const alias = getSynthesizedClone(temp); + alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes; + classAliases[getOriginalNodeId(node)] = alias; + } + + // To preserve the behavior of the old emitter, we explicitly indent + // the body of a class with static initializers. + setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression)); + expressions.push(startOnNewLine(createAssignment(temp, classExpression))); + // Add any pending expressions leftover from elided or relocated computed property names + addRange(expressions, map(pendingExpressions, startOnNewLine)); + addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp)); + expressions.push(startOnNewLine(temp)); + + pendingExpressions = savedPendingExpressions; + return inlineExpressions(expressions); + } + } + + pendingExpressions = savedPendingExpressions; + return classExpression; + } + + function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { + const members: ClassElement[] = []; + const constructor = transformConstructor(node, isDerivedClass); + if (constructor) { + members.push(constructor); + } + addRange(members, visitNodes(node.members, classElementVisitor, isClassElement)); + return setTextRange(createNodeArray(members), /*location*/ node.members); + } + + function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { + const constructor = visitNode(getFirstConstructorWithBody(node), visitor, isConstructorDeclaration); + const containsPropertyInitializer = forEach(node.members, isInitializedProperty); + if (!containsPropertyInitializer) { + return constructor; + } + const parameters = visitParameterList(constructor ? constructor.parameters : undefined, visitor, context); + const body = transformConstructorBody(node, constructor, isDerivedClass); + if (!body) { + return undefined; + } + return startOnNewLine( + setOriginalNode( + setTextRange( + createConstructor( + /*decorators*/ undefined, + /*modifiers*/ undefined, + parameters, + body + ), + constructor || node + ), + constructor + ) + ); + } + + function transformConstructorBody(node: ClassDeclaration | ClassExpression, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) { + const properties = getInitializedProperties(node, /*isStatic*/ false); + + // Only generate synthetic constructor when there are property initializers to move. + if (!constructor && !some(properties)) { + return visitFunctionBody(/*node*/ undefined, visitor, context); + } + + resumeLexicalEnvironment(); + + let indexOfFirstStatement = 0; + let statements: Statement[] = []; + + if (!constructor && isDerivedClass) { + // Add a synthetic `super` call: + // + // super(...arguments); + // + statements.push( + createExpressionStatement( + createCall( + createSuper(), + /*typeArguments*/ undefined, + [createSpread(createIdentifier("arguments"))] + ) + ) + ); + } + + if (constructor) { + indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor); + } + + // Add the property initializers. Transforms this: + // + // public x = 1; + // + // Into this: + // + // constructor() { + // this.x = 1; + // } + // + addInitializedPropertyStatements(statements, properties, createThis()); + + // Add existing statements, skipping the initial super call. + if (constructor) { + addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement)); + } + + statements = mergeLexicalEnvironment(statements, endLexicalEnvironment()); + + return setTextRange( + createBlock( + setTextRange( + createNodeArray(statements), + /*location*/ constructor ? constructor.body!.statements : node.members + ), + /*multiLine*/ true + ), + /*location*/ constructor ? constructor.body : undefined + ); + } + + /** + * Generates assignment statements for property initializers. + * + * @param properties An array of property declarations to transform. + * @param receiver The receiver on which each property should be assigned. + */ + function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray, receiver: LeftHandSideExpression) { + for (const property of properties) { + const statement = createExpressionStatement(transformInitializedProperty(property, receiver)); + setSourceMapRange(statement, moveRangePastModifiers(property)); + setCommentRange(statement, property); + setOriginalNode(statement, property); + statements.push(statement); + } + } + + /** + * Generates assignment expressions for property initializers. + * + * @param properties An array of property declarations to transform. + * @param receiver The receiver on which each property should be assigned. + */ + function generateInitializedPropertyExpressions(properties: ReadonlyArray, receiver: LeftHandSideExpression) { + const expressions: Expression[] = []; + for (const property of properties) { + const expression = transformInitializedProperty(property, receiver); + startOnNewLine(expression); + setSourceMapRange(expression, moveRangePastModifiers(property)); + setCommentRange(expression, property); + setOriginalNode(expression, property); + expressions.push(expression); + } + + return expressions; + } + + /** + * Transforms a property initializer into an assignment statement. + * + * @param property The property declaration. + * @param receiver The object receiving the property assignment. + */ + function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) { + // We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name) + const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression) + ? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name)) + : property.name; + const initializer = visitNode(property.initializer!, visitor, isExpression); + const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName); + + return createAssignment(memberAccess, initializer); + } + + function enableSubstitutionForClassAliases() { + if ((enabledSubstitutions & ESNextSubstitutionFlags.ClassAliases) === 0) { + enabledSubstitutions |= ESNextSubstitutionFlags.ClassAliases; + + // We need to enable substitutions for identifiers. This allows us to + // substitute class names inside of a class declaration. + context.enableSubstitution(SyntaxKind.Identifier); + + // Keep track of class aliases. + classAliases = []; + } + } + + function visitAwaitExpression(node: AwaitExpression): Expression { if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) { return setOriginalNode( @@ -303,6 +685,19 @@ namespace ts { return visitEachChild(node, visitor, context); } + function visitVariableStatement(node: VariableStatement) { + const savedPendingStatements = pendingStatements; + pendingStatements = []; + + const visitedNode = visitEachChild(node, visitor, context); + const statement = some(pendingStatements) ? + [visitedNode, ...pendingStatements] : + visitedNode; + + pendingStatements = savedPendingStatements; + return statement; + } + function visitForStatement(node: ForStatement): VisitResult { return updateFor( node, @@ -728,6 +1123,12 @@ namespace ts { function transformFunctionBody(node: FunctionDeclaration | FunctionExpression | ConstructorDeclaration | MethodDeclaration | AccessorDeclaration): FunctionBody; function transformFunctionBody(node: ArrowFunction): ConciseBody; function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody { + // The function only needs to be transformed if there are object rest parameters. + // It's not enough to rely on the ContainsESNext transform flag check to transform function bodies + // because any function containing PropertyDeclaration nodes will contain ESNext syntax. + if (!(node.transformFlags & TransformFlags.ContainsObjectRestOrSpread)) { + return (node.body && visitFunctionBody(node.body, visitor, context)) || createBlock([]); + } resumeLexicalEnvironment(); let statementOffset = 0; const statements: Statement[] = []; @@ -835,7 +1236,7 @@ namespace ts { */ function onSubstituteNode(hint: EmitHint, node: Node) { node = previousOnSubstituteNode(hint, node); - if (hint === EmitHint.Expression && enclosingSuperContainerFlags) { + if (hint === EmitHint.Expression) { return substituteExpression(node); } return node; @@ -849,12 +1250,14 @@ namespace ts { return substituteElementAccessExpression(node); case SyntaxKind.CallExpression: return substituteCallExpression(node); + case SyntaxKind.Identifier: + return substituteExpressionIdentifier(node as Identifier); } return node; } function substitutePropertyAccessExpression(node: PropertyAccessExpression) { - if (node.expression.kind === SyntaxKind.SuperKeyword) { + if (enclosingSuperContainerFlags && node.expression.kind === SyntaxKind.SuperKeyword) { return setTextRange( createPropertyAccess( createFileLevelUniqueName("_super"), @@ -866,7 +1269,7 @@ namespace ts { } function substituteElementAccessExpression(node: ElementAccessExpression) { - if (node.expression.kind === SyntaxKind.SuperKeyword) { + if (enclosingSuperContainerFlags && node.expression.kind === SyntaxKind.SuperKeyword) { return createSuperElementAccessInAsyncMethod( node.argumentExpression, node @@ -876,6 +1279,9 @@ namespace ts { } function substituteCallExpression(node: CallExpression): Expression { + if (!enclosingSuperContainerFlags) { + return node; + } const expression = node.expression; if (isSuperProperty(expression)) { const argumentExpression = isPropertyAccessExpression(expression) @@ -893,6 +1299,34 @@ namespace ts { return node; } + function substituteExpressionIdentifier(node: Identifier): Expression { + return trySubstituteClassAlias(node) || node; + } + + function trySubstituteClassAlias(node: Identifier): Expression | undefined { + if (enabledSubstitutions & ESNextSubstitutionFlags.ClassAliases) { + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ConstructorReferenceInClass) { + // Due to the emit for class decorators, any reference to the class from inside of the class body + // must instead be rewritten to point to a temporary variable to avoid issues with the double-bind + // behavior of class names in ES6. + // Also, when emitting statics for class expressions, we must substitute a class alias for + // constructor references in static property initializers. + const declaration = resolver.getReferencedValueDeclaration(node); + if (declaration) { + const classAlias = classAliases[declaration.id!]; // TODO: GH#18217 + if (classAlias) { + const clone = getSynthesizedClone(classAlias); + setSourceMapRange(clone, node); + setCommentRange(clone, node); + return clone; + } + } + } + } + + return undefined; + } + function isSuperContainer(node: Node) { const kind = node.kind; return kind === SyntaxKind.ClassDeclaration diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index bdb1c7cbe9373..4e1982c2019d3 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -64,6 +64,7 @@ namespace ts { let currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock; let currentNameScope: ClassDeclaration | undefined; let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap | undefined; + let currentClassHasParameterProperties: boolean | undefined; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -83,12 +84,6 @@ namespace ts { */ let applicableSubstitutions: TypeScriptSubstitutionFlags; - /** - * Tracks what computed name expressions originating from elided names must be inlined - * at the next execution site, in document order - */ - let pendingExpressions: Expression[] | undefined; - return transformSourceFileOrBundle; function transformSourceFileOrBundle(node: SourceFile | Bundle) { @@ -136,6 +131,7 @@ namespace ts { const savedCurrentScope = currentLexicalScope; const savedCurrentNameScope = currentNameScope; const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName; + const savedCurrentClassHasParameterProperties = currentClassHasParameterProperties; // Handle state changes before visiting a node. onBeforeVisitNode(node); @@ -149,6 +145,7 @@ namespace ts { currentLexicalScope = savedCurrentScope; currentNameScope = savedCurrentNameScope; + currentClassHasParameterProperties = savedCurrentClassHasParameterProperties; return visited; } @@ -333,6 +330,9 @@ namespace ts { return undefined; case SyntaxKind.PropertyDeclaration: + // Property declarations are not TypeScript syntax, but they must be visited + // for the decorator transformation. + return visitPropertyDeclaration(node as PropertyDeclaration); case SyntaxKind.IndexSignature: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: @@ -449,7 +449,6 @@ namespace ts { // - decorators // - optional `implements` heritage clause // - parameter property assignments in the constructor - // - property declarations // - index signatures // - method overload signatures return visitClassDeclaration(node); @@ -461,7 +460,6 @@ namespace ts { // - decorators // - optional `implements` heritage clause // - parameter property assignments in the constructor - // - property declarations // - index signatures // - method overload signatures return visitClassExpression(node); @@ -612,16 +610,12 @@ namespace ts { * * This function will only be called when one of the following conditions are met: * - The class has decorators. - * - The class has property declarations with initializers. * - The class contains a constructor that contains parameters with accessibility modifiers. * - The class is an export in a TypeScript namespace. * * @param node The node to transform. */ function visitClassDeclaration(node: ClassDeclaration): VisitResult { - const savedPendingExpressions = pendingExpressions; - pendingExpressions = undefined; - const staticProperties = getInitializedProperties(node, /*isStatic*/ true); const facts = getClassFacts(node, staticProperties); @@ -631,25 +625,11 @@ namespace ts { const name = node.name || (facts & ClassFacts.NeedsName ? getGeneratedNameForNode(node) : undefined); const classStatement = facts & ClassFacts.HasConstructorDecorators - ? createClassDeclarationHeadWithDecorators(node, name, facts) + ? createClassDeclarationHeadWithDecorators(node, name) : createClassDeclarationHeadWithoutDecorators(node, name, facts); let statements: Statement[] = [classStatement]; - // Write any pending expressions from elided or moved computed property names - if (some(pendingExpressions)) { - statements.push(createExpressionStatement(inlineExpressions(pendingExpressions!))); - } - pendingExpressions = savedPendingExpressions; - - // Emit static property assignment. Because classDeclaration is lexically evaluated, - // it is safe to emit static property assignment after classDeclaration - // From ES6 specification: - // HasLexicalDeclaration (N) : Determines if the argument identifier has a binding in this environment record that was created using - // a lexical declaration such as a LexicalDeclaration or a ClassDeclaration. - if (facts & ClassFacts.HasStaticInitializedProperties) { - addInitializedPropertyStatements(statements, staticProperties, facts & ClassFacts.UseImmediatelyInvokedFunctionExpression ? getInternalName(node) : getLocalName(node)); - } // Write any decorators of the node. addClassElementDecorationStatements(statements, node, /*isStatic*/ false); @@ -752,7 +732,7 @@ namespace ts { name, /*typeParameters*/ undefined, visitNodes(node.heritageClauses, visitor, isHeritageClause), - transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0) + transformClassMembers(node) ); // To better align with the old emitter, we should not emit a trailing source map @@ -772,7 +752,7 @@ namespace ts { * Transforms a decorated class declaration and appends the resulting statements. If * the class requires an alias to avoid issues with double-binding, the alias is returned. */ - function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined, facts: ClassFacts) { + function createClassDeclarationHeadWithDecorators(node: ClassDeclaration, name: Identifier | undefined) { // When we emit an ES6 class that has a class decorator, we must tailor the // emit to certain specific cases. // @@ -867,7 +847,7 @@ namespace ts { // ${members} // } const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); - const members = transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0); + const members = transformClassMembers(node); const classExpression = createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members); setOriginalNode(classExpression, node); setTextRange(classExpression, location); @@ -894,55 +874,24 @@ namespace ts { * Transforms a class expression with TypeScript syntax into compatible ES6. * * This function will only be called when one of the following conditions are met: - * - The class has property declarations with initializers. * - The class contains a constructor that contains parameters with accessibility modifiers. * * @param node The node to transform. */ function visitClassExpression(node: ClassExpression): Expression { - const savedPendingExpressions = pendingExpressions; - pendingExpressions = undefined; - - const staticProperties = getInitializedProperties(node, /*isStatic*/ true); - const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause); - const members = transformClassMembers(node, some(heritageClauses, c => c.token === SyntaxKind.ExtendsKeyword)); + const members = transformClassMembers(node); const classExpression = createClassExpression( /*modifiers*/ undefined, node.name, /*typeParameters*/ undefined, - heritageClauses, + node.heritageClauses, members ); setOriginalNode(classExpression, node); setTextRange(classExpression, node); - if (some(staticProperties) || some(pendingExpressions)) { - const expressions: Expression[] = []; - const isClassWithConstructorReference = resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference; - const temp = createTempVariable(hoistVariableDeclaration, !!isClassWithConstructorReference); - if (isClassWithConstructorReference) { - // record an alias as the class name is not in scope for statics. - enableSubstitutionForClassAliases(); - const alias = getSynthesizedClone(temp); - alias.autoGenerateFlags &= ~GeneratedIdentifierFlags.ReservedInNestedScopes; - classAliases[getOriginalNodeId(node)] = alias; - } - - // To preserve the behavior of the old emitter, we explicitly indent - // the body of a class with static initializers. - setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression)); - expressions.push(startOnNewLine(createAssignment(temp, classExpression))); - // Add any pending expressions leftover from elided or relocated computed property names - addRange(expressions, map(pendingExpressions, startOnNewLine)); - pendingExpressions = savedPendingExpressions; - addRange(expressions, generateInitializedPropertyExpressions(staticProperties, temp)); - expressions.push(startOnNewLine(temp)); - return inlineExpressions(expressions); - } - - pendingExpressions = savedPendingExpressions; return classExpression; } @@ -950,61 +899,81 @@ namespace ts { * Transforms the members of a class. * * @param node The current class. - * @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'. */ - function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { + function transformClassMembers(node: ClassDeclaration | ClassExpression) { const members: ClassElement[] = []; - const constructor = transformConstructor(node, isDerivedClass); - if (constructor) { - members.push(constructor); - } - - addRange(members, visitNodes(node.members, classElementVisitor, isClassElement)); - return setTextRange(createNodeArray(members), /*location*/ node.members); - } - - /** - * Transforms (or creates) a constructor for a class. - * - * @param node The current class. - * @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'. - */ - function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) { - // Check if we have property assignment inside class declaration. - // If there is a property assignment, we need to emit constructor whether users define it or not - // If there is no property assignment, we can omit constructor if users do not define it + const existingMembers = visitNodes(node.members, classElementVisitor, isClassElement); const constructor = getFirstConstructorWithBody(node); - const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty); - const hasParameterPropertyAssignments = constructor && - constructor.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax && - forEach(constructor.parameters, isParameterWithPropertyAssignment); - - // If the class does not contain nodes that require a synthesized constructor, - // accept the current constructor if it exists. - if (!hasInstancePropertyWithInitializer && !hasParameterPropertyAssignments) { - return visitEachChild(constructor, visitor, context); - } - - const parameters = transformConstructorParameters(constructor); - const body = transformConstructorBody(node, constructor, isDerivedClass); + const parametersWithPropertyAssignments = + constructor && !!(constructor.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax) + ? filter(constructor.parameters, isParameterPropertyDeclaration) + : undefined; + if (some(parametersWithPropertyAssignments) && constructor) { + currentClassHasParameterProperties = true; + + // Create property declarations for constructor parameter properties. + addRange( + members, + parametersWithPropertyAssignments.map(param => + createProperty( + /*decorators*/ undefined, + /*modifiers*/ undefined, + param.name, + /*questionOrExclamationToken*/ undefined, + /*type*/ undefined, + /*initializer*/ undefined + ) + ) + ); - // constructor(${parameters}) { - // ${body} - // } - return startOnNewLine( - setOriginalNode( - setTextRange( - createConstructor( + const parameters = transformConstructorParameters(constructor); + const body = transformConstructorBody(node.members, constructor, parametersWithPropertyAssignments); + members.push(startOnNewLine( + setOriginalNode( + setTextRange( + createConstructor( /*decorators*/ undefined, /*modifiers*/ undefined, - parameters, - body + parameters, + body + ), + constructor ), - constructor || node - ), - constructor - ) - ); + constructor + ) + )); + addRange( + members, + visitNodes( + existingMembers, + member => { + if (isPropertyDeclaration(member) && !hasStaticModifier(member) && !!member.initializer) { + const updated = updateProperty( + member, + member.decorators, + member.modifiers, + member.name, + member.questionToken, + member.type, + /*initializer*/ undefined + ); + setCommentRange(updated, node); + setSourceMapRange(updated, node); + return updated; + } + return member; + }, + isClassElement + ) + ); + } + else { + if (constructor) { + members.push(visitEachChild(constructor, visitor, context)); + } + addRange(members, existingMembers); + } + return setTextRange(createNodeArray(members), /*location*/ node.members); } /** @@ -1013,7 +982,7 @@ namespace ts { * * @param constructor The constructor declaration. */ - function transformConstructorParameters(constructor: ConstructorDeclaration | undefined) { + function transformConstructorParameters(constructor: ConstructorDeclaration) { // The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation: // If constructor is empty, then // If ClassHeritag_eopt is present and protoParent is not null, then @@ -1034,70 +1003,54 @@ namespace ts { } /** - * Transforms (or creates) a constructor body for a class with parameter property - * assignments or instance property initializers. + * Transforms (or creates) a constructor body for a class with parameter property assignments. * * @param node The current class. * @param constructor The current class constructor. - * @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'. */ - function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration | undefined, isDerivedClass: boolean) { + function transformConstructorBody(members: NodeArray, constructor: ConstructorDeclaration, propertyAssignments: ReadonlyArray) { let statements: Statement[] = []; let indexOfFirstStatement = 0; resumeLexicalEnvironment(); - if (constructor) { - indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements); - - // Add parameters with property assignments. Transforms this: - // - // constructor (public x, public y) { - // } - // - // Into this: - // - // constructor (x, y) { - // this.x = x; - // this.y = y; - // } - // - const propertyAssignments = getParametersWithPropertyAssignments(constructor); - addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment)); - } - else if (isDerivedClass) { - // Add a synthetic `super` call: - // - // super(...arguments); - // - statements.push( - createExpressionStatement( - createCall( - createSuper(), - /*typeArguments*/ undefined, - [createSpread(createIdentifier("arguments"))] - ) - ) - ); - } + indexOfFirstStatement = addPrologueDirectivesAndInitialSuperCall(constructor, statements, visitor); - // Add the property initializers. Transforms this: + // Add parameters with property assignments. Transforms this: // - // public x = 1; + // constructor (public x, public y) { + // } // // Into this: // - // constructor() { - // this.x = 1; + // constructor (x, y) { + // this.x = x; + // this.y = y; // } // - const properties = getInitializedProperties(node, /*isStatic*/ false); - addInitializedPropertyStatements(statements, properties, createThis()); + addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment)); + + // Get property initializers. + const classBodyProperties = members.filter(member => isPropertyDeclaration(member) && !hasStaticModifier(member) && !!member.initializer) as PropertyDeclaration[]; + addRange(statements, classBodyProperties.map( + prop => { + const name = prop.name; + const lhs = (!isComputedPropertyName(name) || isSimpleInlineableExpression(name.expression)) ? + createMemberAccessForPropertyName(createThis(), name, prop) : + createElementAccess(createThis(), getGeneratedNameForNode(name)); + const initializerNode = createExpressionStatement( + createAssignment(lhs, prop.initializer!) + ); + setOriginalNode(initializerNode, prop); + setTextRange(initializerNode, prop); + setCommentRange(initializerNode, prop); + setSourceMapRange(initializerNode, prop); + return initializerNode; + } + )); - if (constructor) { - // The class already had a constructor, so we should add the existing statements, skipping the initial super call. - addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement)); - } + // Add the existing statements, skipping the initial super call. + addRange(statements, visitNodes(constructor.body!.statements, visitor, isStatement, indexOfFirstStatement)); // End the lexical environment. statements = mergeLexicalEnvironment(statements, endLexicalEnvironment()); @@ -1105,7 +1058,7 @@ namespace ts { createBlock( setTextRange( createNodeArray(statements), - /*location*/ constructor ? constructor.body!.statements : node.members + /*location*/ constructor ? constructor.body!.statements : members ), /*multiLine*/ true ), @@ -1113,61 +1066,16 @@ namespace ts { ); } - /** - * Adds super call and preceding prologue directives into the list of statements. - * - * @param ctor The constructor node. - * @returns index of the statement that follows super call - */ - function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[]): number { - if (ctor.body) { - const statements = ctor.body.statements; - // add prologue directives to the list (if any) - const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor); - if (index === statements.length) { - // list contains nothing but prologue directives (or empty) - exit - return index; - } - - const statement = statements[index]; - if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((statement).expression)) { - result.push(visitNode(statement, visitor, isStatement)); - return index + 1; - } - - return index; - } - - return 0; - } - - /** - * Gets all parameters of a constructor that should be transformed into property assignments. - * - * @param node The constructor node. - */ - function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ReadonlyArray { - return filter(node.parameters, isParameterWithPropertyAssignment); - } - - /** - * Determines whether a parameter should be transformed into a property assignment. - * - * @param parameter The parameter node. - */ - function isParameterWithPropertyAssignment(parameter: ParameterDeclaration) { - return hasModifier(parameter, ModifierFlags.ParameterPropertyModifier) - && isIdentifier(parameter.name); - } - /** * Transforms a parameter into a property assignment statement. * * @param node The parameter declaration. */ - function transformParameterWithPropertyAssignment(node: ParameterDeclaration) { - Debug.assert(isIdentifier(node.name)); - const name = node.name as Identifier; + function transformParameterWithPropertyAssignment(node: ParameterPropertyDeclaration) { + const name = node.name; + if (!isIdentifier(name)) { + return undefined; + } const propertyName = getMutableClone(name); setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap); @@ -1196,99 +1104,6 @@ namespace ts { ); } - /** - * Gets all property declarations with initializers on either the static or instance side of a class. - * - * @param node The class node. - * @param isStatic A value indicating whether to get properties from the static or instance side of the class. - */ - function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray { - return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty); - } - - /** - * Gets a value indicating whether a class element is a static property declaration with an initializer. - * - * @param member The class element node. - */ - function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration { - return isInitializedProperty(member, /*isStatic*/ true); - } - - /** - * Gets a value indicating whether a class element is an instance property declaration with an initializer. - * - * @param member The class element node. - */ - function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration { - return isInitializedProperty(member, /*isStatic*/ false); - } - - /** - * Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer. - * - * @param member The class element node. - * @param isStatic A value indicating whether the member should be a static or instance member. - */ - function isInitializedProperty(member: ClassElement, isStatic: boolean) { - return member.kind === SyntaxKind.PropertyDeclaration - && isStatic === hasModifier(member, ModifierFlags.Static) - && (member).initializer !== undefined; - } - - /** - * Generates assignment statements for property initializers. - * - * @param properties An array of property declarations to transform. - * @param receiver The receiver on which each property should be assigned. - */ - function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray, receiver: LeftHandSideExpression) { - for (const property of properties) { - const statement = createExpressionStatement(transformInitializedProperty(property, receiver)); - setSourceMapRange(statement, moveRangePastModifiers(property)); - setCommentRange(statement, property); - setOriginalNode(statement, property); - statements.push(statement); - } - } - - /** - * Generates assignment expressions for property initializers. - * - * @param properties An array of property declarations to transform. - * @param receiver The receiver on which each property should be assigned. - */ - function generateInitializedPropertyExpressions(properties: ReadonlyArray, receiver: LeftHandSideExpression) { - const expressions: Expression[] = []; - for (const property of properties) { - const expression = transformInitializedProperty(property, receiver); - startOnNewLine(expression); - setSourceMapRange(expression, moveRangePastModifiers(property)); - setCommentRange(expression, property); - setOriginalNode(expression, property); - expressions.push(expression); - } - - return expressions; - } - - /** - * Transforms a property initializer into an assignment statement. - * - * @param property The property declaration. - * @param receiver The object receiving the property assignment. - */ - function transformInitializedProperty(property: PropertyDeclaration, receiver: LeftHandSideExpression) { - // We generate a name here in order to reuse the value cached by the relocated computed name expression (which uses the same generated name) - const propertyName = isComputedPropertyName(property.name) && !isSimpleInlineableExpression(property.name.expression) - ? updateComputedPropertyName(property.name, getGeneratedNameForNode(property.name)) - : property.name; - const initializer = visitNode(property.initializer!, visitor, isExpression); - const memberAccess = createMemberAccessForPropertyName(receiver, propertyName, /*location*/ propertyName); - - return createAssignment(memberAccess, initializer); - } - /** * Gets either the static or instance members of a class that are decorated, or have * parameters that are decorated. @@ -2115,15 +1930,6 @@ namespace ts { ); } - /** - * A simple inlinable expression is an expression which can be copied into multiple locations - * without risk of repeating any sideeffects and whose value could not possibly change between - * any such locations - */ - function isSimpleInlineableExpression(expression: Expression) { - return !isIdentifier(expression) && isSimpleCopiableExpression(expression) || - isWellKnownSymbolSyntactically(expression); - } /** * Gets an expression that represents a property name. For a computed property, a @@ -2146,26 +1952,6 @@ namespace ts { } } - /** - * If the name is a computed property, this function transforms it, then either returns an expression which caches the - * value of the result or the expression itself if the value is either unused or safe to inline into multiple locations - * @param shouldHoist Does the expression need to be reused? (ie, for an initializer or a decorator) - * @param omitSimple Should expressions with no observable side-effects be elided? (ie, the expression is not hoisted for a decorator or initializer and is a literal) - */ - function getPropertyNameExpressionIfNeeded(name: PropertyName, shouldHoist: boolean, omitSimple: boolean): Expression | undefined { - if (isComputedPropertyName(name)) { - const expression = visitNode(name.expression, visitor, isExpression); - const innerExpression = skipPartiallyEmittedExpressions(expression); - const inlinable = isSimpleInlineableExpression(innerExpression); - if (!inlinable && shouldHoist) { - const generatedName = getGeneratedNameForNode(name); - hoistVariableDeclaration(generatedName); - return createAssignment(generatedName, expression); - } - return (omitSimple && (inlinable || isIdentifier(innerExpression))) ? undefined : expression; - } - } - /** * Visits the property name of a class element, for use when emitting property * initializers. For a computed property on a node with decorators, a temporary @@ -2175,18 +1961,20 @@ namespace ts { */ function visitPropertyNameOfClassElement(member: ClassElement): PropertyName { const name = member.name!; - let expr = getPropertyNameExpressionIfNeeded(name, some(member.decorators), /*omitSimple*/ false); - if (expr) { // expr only exists if `name` is a computed property name - // Inline any pending expressions from previous elided or relocated computed property name expressions in order to preserve execution order - if (some(pendingExpressions)) { - expr = inlineExpressions([...pendingExpressions, expr]); - pendingExpressions.length = 0; + // Computed property names need to be transformed into a hoisted variable when they are used more than once. + // The names are used more than once when: + // - the property is non-static and its initializer is moved to the constructor (when there are paramerty property assignments). + // - the property has a decorator. + if (isComputedPropertyName(name) && ((!hasStaticModifier(member) && currentClassHasParameterProperties) || some(member.decorators))) { + const expression = visitNode(name.expression, visitor, isExpression); + const innerExpression = skipPartiallyEmittedExpressions(expression); + if (!isSimpleInlineableExpression(innerExpression)) { + const generatedName = getGeneratedNameForNode(name); + hoistVariableDeclaration(generatedName); + return updateComputedPropertyName(name, createAssignment(generatedName, expression)); } - return updateComputedPropertyName(name as ComputedPropertyName, expr); - } - else { - return name; } + return visitNode(name, visitor, isPropertyName); } /** @@ -2239,12 +2027,23 @@ namespace ts { return !nodeIsMissing(node.body); } - function visitPropertyDeclaration(node: PropertyDeclaration): undefined { - const expr = getPropertyNameExpressionIfNeeded(node.name, some(node.decorators) || !!node.initializer, /*omitSimple*/ true); - if (expr && !isSimpleInlineableExpression(expr)) { - (pendingExpressions || (pendingExpressions = [])).push(expr); + function visitPropertyDeclaration(node: PropertyDeclaration) { + const updated = updateProperty( + node, + /*decorators*/ undefined, + visitNodes(node.modifiers, visitor, isModifier), + visitPropertyNameOfClassElement(node), + /*questionOrExclamationToken*/ undefined, + /*type*/ undefined, + visitNode(node.initializer, visitor) + ); + if (updated !== node) { + // While we emit the source map for the node after skipping decorators and modifiers, + // we need to emit the comments for the original range. + setCommentRange(updated, node); + setSourceMapRange(updated, moveRangePastDecorators(node)); } - return undefined; + return updated; } function visitConstructor(node: ConstructorDeclaration) { diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index 09239d7765fd9..f5f8a298a490b 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -240,6 +240,47 @@ namespace ts { isIdentifier(expression); } + /** + * A simple inlinable expression is an expression which can be copied into multiple locations + * without risk of repeating any sideeffects and whose value could not possibly change between + * any such locations + */ + export function isSimpleInlineableExpression(expression: Expression) { + return !isIdentifier(expression) && isSimpleCopiableExpression(expression) || + isWellKnownSymbolSyntactically(expression); + } + + /** + * Adds super call and preceding prologue directives into the list of statements. + * + * @param ctor The constructor node. + * @param result The list of statements. + * @param visitor The visitor to apply to each node added to the result array. + * @returns index of the statement that follows super call + */ + export function addPrologueDirectivesAndInitialSuperCall(ctor: ConstructorDeclaration, result: Statement[], visitor: Visitor): number { + if (ctor.body) { + const statements = ctor.body.statements; + // add prologue directives to the list (if any) + const index = addPrologue(result, statements, /*ensureUseStrict*/ false, visitor); + if (index === statements.length) { + // list contains nothing but prologue directives (or empty) - exit + return index; + } + + const statement = statements[index]; + if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((statement).expression)) { + result.push(visitNode(statement, visitor, isStatement)); + return index + 1; + } + + return index; + } + + return 0; + } + + /** * @param input Template string input strings * @param args Names which need to be made file-level unique @@ -255,4 +296,43 @@ namespace ts { return result; }; } + + /** + * Gets all property declarations with initializers on either the static or instance side of a class. + * + * @param node The class node. + * @param isStatic A value indicating whether to get properties from the static or instance side of the class. + */ + export function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray { + return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty); + } + + /** + * Gets a value indicating whether a class element is a static property declaration with an initializer. + * + * @param member The class element node. + */ + export function isStaticInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } { + return isInitializedProperty(member) && hasStaticModifier(member); + } + + /** + * Gets a value indicating whether a class element is an instance property declaration with an initializer. + * + * @param member The class element node. + */ + export function isInstanceInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } { + return isInitializedProperty(member) && !hasStaticModifier(member); + } + + /** + * Gets a value indicating whether a class element is either a static or an instance property declaration with an initializer. + * + * @param member The class element node. + * @param isStatic A value indicating whether the member should be a static or instance member. + */ + export function isInitializedProperty(member: ClassElement): member is PropertyDeclaration & { initializer: Expression; } { + return member.kind === SyntaxKind.PropertyDeclaration + && (member).initializer !== undefined; + } } \ No newline at end of file From 861936ed4e155e858f666eb84f728540ad12dc1f Mon Sep 17 00:00:00 2001 From: Joey Watts Date: Thu, 27 Sep 2018 14:06:37 -0400 Subject: [PATCH 2/2] Baseline changes for ESNext Class Property Transformation (#4) * Add baselines * Update baselines Signed-off-by: Joseph Watts --- ...tPrivateSymbolCausesVarDeclarationEmit2.js | 4 +- .../decoratorsOnComputedProperties.js | 145 +++++++++--------- .../baselines/reference/dynamicNamesErrors.js | 2 + .../reference/importCallExpression4ESNext.js | 4 +- .../importCallExpressionAsyncESNext.js | 12 +- .../reference/importCallExpressionInAMD4.js | 8 +- .../reference/importCallExpressionInCJS5.js | 8 +- .../importCallExpressionInSystem4.js | 8 +- .../reference/importCallExpressionInUMD4.js | 8 +- .../potentiallyUncalledDecorators.js | 11 ++ tests/baselines/reference/spreadMethods.js | 4 +- .../reference/systemModuleTargetES6.js | 2 +- .../taggedTemplatesWithTypeArguments2.js | 3 + ...rectly.rewrittenNamespaceFollowingClass.js | 6 +- tests/baselines/reference/uniqueSymbols.js | 44 +++--- .../reference/uniqueSymbolsDeclarations.js | 44 +++--- .../uniqueSymbolsDeclarationsErrors.js | 2 + .../reference/uniqueSymbolsErrors.js | 3 + ...arationEmitUniqueSymbolPartialStatement.js | 2 +- 19 files changed, 159 insertions(+), 161 deletions(-) diff --git a/tests/baselines/reference/declarationEmitPrivateSymbolCausesVarDeclarationEmit2.js b/tests/baselines/reference/declarationEmitPrivateSymbolCausesVarDeclarationEmit2.js index b78c104f4682b..51ffc3a02fac7 100644 --- a/tests/baselines/reference/declarationEmitPrivateSymbolCausesVarDeclarationEmit2.js +++ b/tests/baselines/reference/declarationEmitPrivateSymbolCausesVarDeclarationEmit2.js @@ -34,8 +34,8 @@ var C = /** @class */ (function () { } return C; }()); -_a = a_1.x; exports.C = C; +_a = a_1.x; //// [c.js] "use strict"; var __extends = (this && this.__extends) || (function () { @@ -64,8 +64,8 @@ var D = /** @class */ (function (_super) { } return D; }(b_1.C)); -_a = a_1.x; exports.D = D; +_a = a_1.x; //// [a.d.ts] diff --git a/tests/baselines/reference/decoratorsOnComputedProperties.js b/tests/baselines/reference/decoratorsOnComputedProperties.js index 2af6b460c0518..3f0444b310aaa 100644 --- a/tests/baselines/reference/decoratorsOnComputedProperties.js +++ b/tests/baselines/reference/decoratorsOnComputedProperties.js @@ -196,7 +196,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; -var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21; +var _a, _b, _c, _d; +var _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21; function x(o, k) { } let i = 0; function foo() { return ++i + ""; } @@ -209,11 +210,11 @@ class A { this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_b] = null; - this[_d] = null; + this[_f] = null; + this[_h] = null; } } -foo(), _a = foo(), _b = foo(), _c = fieldNameB, _d = fieldNameC; +foo(), _e = foo(), _f = foo(), _g = fieldNameB, _h = fieldNameC; __decorate([ x ], A.prototype, "property", void 0); @@ -228,42 +229,42 @@ __decorate([ ], A.prototype, Symbol.iterator, void 0); __decorate([ x -], A.prototype, _a, void 0); +], A.prototype, _e, void 0); __decorate([ x -], A.prototype, _b, void 0); +], A.prototype, _f, void 0); __decorate([ x -], A.prototype, _c, void 0); +], A.prototype, _g, void 0); __decorate([ x -], A.prototype, _d, void 0); -void (_j = class B { +], A.prototype, _h, void 0); +void (_a = class B { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_f] = null; - this[_h] = null; + this[_k] = null; + this[_m] = null; } }, foo(), - _e = foo(), - _f = foo(), - _g = fieldNameB, - _h = fieldNameC, - _j); + _j = foo(), + _k = foo(), + _l = fieldNameB, + _m = fieldNameC, + _a); class C { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_l] = null; - this[_o] = null; + this[_p] = null; + this[_r] = null; } - [(foo(), _k = foo(), _l = foo(), _m = fieldNameB, _o = fieldNameC, "some" + "method")]() { } + [(foo(), _o = foo(), _p = foo(), _q = fieldNameB, _r = fieldNameC, "some" + "method")]() { } } __decorate([ x @@ -279,26 +280,26 @@ __decorate([ ], C.prototype, Symbol.iterator, void 0); __decorate([ x -], C.prototype, _k, void 0); +], C.prototype, _o, void 0); __decorate([ x -], C.prototype, _l, void 0); +], C.prototype, _p, void 0); __decorate([ x -], C.prototype, _m, void 0); +], C.prototype, _q, void 0); __decorate([ x -], C.prototype, _o, void 0); +], C.prototype, _r, void 0); void class D { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_q] = null; - this[_s] = null; + this[_t] = null; + this[_v] = null; } - [(foo(), _p = foo(), _q = foo(), _r = fieldNameB, _s = fieldNameC, "some" + "method")]() { } + [(foo(), _s = foo(), _t = foo(), _u = fieldNameB, _v = fieldNameC, "some" + "method")]() { } }; class E { constructor() { @@ -306,12 +307,12 @@ class E { this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_u] = null; - this[_w] = null; + this[_x] = null; + this[_z] = null; } - [(foo(), _t = foo(), _u = foo(), "some" + "method")]() { } + [(foo(), _w = foo(), _x = foo(), "some" + "method")]() { } } -_v = fieldNameB, _w = fieldNameC; +_y = fieldNameB, _z = fieldNameC; __decorate([ x ], E.prototype, "property", void 0); @@ -326,43 +327,43 @@ __decorate([ ], E.prototype, Symbol.iterator, void 0); __decorate([ x -], E.prototype, _t, void 0); +], E.prototype, _w, void 0); __decorate([ x -], E.prototype, _u, void 0); +], E.prototype, _x, void 0); __decorate([ x -], E.prototype, _v, void 0); +], E.prototype, _y, void 0); __decorate([ x -], E.prototype, _w, void 0); -void (_1 = class F { +], E.prototype, _z, void 0); +void (_b = class F { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_y] = null; - this[_0] = null; + this[_1] = null; + this[_3] = null; } - [(foo(), _x = foo(), _y = foo(), "some" + "method")]() { } + [(foo(), _0 = foo(), _1 = foo(), "some" + "method")]() { } }, - _z = fieldNameB, - _0 = fieldNameC, - _1); + _2 = fieldNameB, + _3 = fieldNameC, + _b); class G { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_3] = null; this[_5] = null; + this[_7] = null; } - [(foo(), _2 = foo(), _3 = foo(), "some" + "method")]() { } - [(_4 = fieldNameB, "some" + "method2")]() { } + [(foo(), _4 = foo(), _5 = foo(), "some" + "method")]() { } + [(_6 = fieldNameB, "some" + "method2")]() { } } -_5 = fieldNameC; +_7 = fieldNameC; __decorate([ x ], G.prototype, "property", void 0); @@ -377,43 +378,43 @@ __decorate([ ], G.prototype, Symbol.iterator, void 0); __decorate([ x -], G.prototype, _2, void 0); +], G.prototype, _4, void 0); __decorate([ x -], G.prototype, _3, void 0); +], G.prototype, _5, void 0); __decorate([ x -], G.prototype, _4, void 0); +], G.prototype, _6, void 0); __decorate([ x -], G.prototype, _5, void 0); -void (_10 = class H { +], G.prototype, _7, void 0); +void (_c = class H { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_7] = null; this[_9] = null; + this[_11] = null; } - [(foo(), _6 = foo(), _7 = foo(), "some" + "method")]() { } - [(_8 = fieldNameB, "some" + "method2")]() { } + [(foo(), _8 = foo(), _9 = foo(), "some" + "method")]() { } + [(_10 = fieldNameB, "some" + "method2")]() { } }, - _9 = fieldNameC, - _10); + _11 = fieldNameC, + _c); class I { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_12] = null; - this[_15] = null; + this[_13] = null; + this[_16] = null; } - [(foo(), _11 = foo(), _12 = foo(), _13 = "some" + "method")]() { } - [(_14 = fieldNameB, "some" + "method2")]() { } + [(foo(), _12 = foo(), _13 = foo(), _14 = "some" + "method")]() { } + [(_15 = fieldNameB, "some" + "method2")]() { } } -_15 = fieldNameC; +_16 = fieldNameC; __decorate([ x ], I.prototype, "property", void 0); @@ -426,32 +427,32 @@ __decorate([ __decorate([ x ], I.prototype, Symbol.iterator, void 0); -__decorate([ - x -], I.prototype, _11, void 0); __decorate([ x ], I.prototype, _12, void 0); __decorate([ x -], I.prototype, _13, null); +], I.prototype, _13, void 0); __decorate([ x -], I.prototype, _14, void 0); +], I.prototype, _14, null); __decorate([ x ], I.prototype, _15, void 0); -void (_21 = class J { +__decorate([ + x +], I.prototype, _16, void 0); +void (_d = class J { constructor() { this["property2"] = 2; this[Symbol.iterator] = null; this["property4"] = 2; this[Symbol.match] = null; - this[_17] = null; - this[_20] = null; + this[_18] = null; + this[_21] = null; } - [(foo(), _16 = foo(), _17 = foo(), _18 = "some" + "method")]() { } - [(_19 = fieldNameB, "some" + "method2")]() { } + [(foo(), _17 = foo(), _18 = foo(), _19 = "some" + "method")]() { } + [(_20 = fieldNameB, "some" + "method2")]() { } }, - _20 = fieldNameC, - _21); + _21 = fieldNameC, + _d); diff --git a/tests/baselines/reference/dynamicNamesErrors.js b/tests/baselines/reference/dynamicNamesErrors.js index ff6bdec604f31..f2b610de875e0 100644 --- a/tests/baselines/reference/dynamicNamesErrors.js +++ b/tests/baselines/reference/dynamicNamesErrors.js @@ -73,9 +73,11 @@ const y = Symbol(); const z = Symbol(); const w = Symbol(); class ClassMemberVisibility { + static [x]; static [y]() { return 0; } static get [z]() { return 0; } static set [w](value) { } + [x]; [y]() { return 0; } get [z]() { return 0; } set [w](value) { } diff --git a/tests/baselines/reference/importCallExpression4ESNext.js b/tests/baselines/reference/importCallExpression4ESNext.js index d5d4a51d69f0b..656f63cf3f21f 100644 --- a/tests/baselines/reference/importCallExpression4ESNext.js +++ b/tests/baselines/reference/importCallExpression4ESNext.js @@ -35,9 +35,7 @@ export function foo() { return "foo"; } export function backup() { return "backup"; } //// [2.js] class C { - constructor() { - this.myModule = import("./0"); - } + myModule = import("./0"); method() { const loadAsync = import("./0"); this.myModule.then(Zero => { diff --git a/tests/baselines/reference/importCallExpressionAsyncESNext.js b/tests/baselines/reference/importCallExpressionAsyncESNext.js index 7d5f2b0ed25a7..8c7b34c00745c 100644 --- a/tests/baselines/reference/importCallExpressionAsyncESNext.js +++ b/tests/baselines/reference/importCallExpressionAsyncESNext.js @@ -43,13 +43,11 @@ export const obj = { } }; export class cl2 { - constructor() { - this.p = { - m: async () => { - const req = await import('./test'); // FOUR - } - }; - } + p = { + m: async () => { + const req = await import('./test'); // FOUR + } + }; } export const l = async () => { const req = await import('./test'); // FIVE diff --git a/tests/baselines/reference/importCallExpressionInAMD4.js b/tests/baselines/reference/importCallExpressionInAMD4.js index 2fe29e5ae065a..64c6be888939e 100644 --- a/tests/baselines/reference/importCallExpressionInAMD4.js +++ b/tests/baselines/reference/importCallExpressionInAMD4.js @@ -63,9 +63,7 @@ define(["require", "exports"], function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class C { - constructor() { - this.myModule = new Promise((resolve_1, reject_1) => { require(["./0"], resolve_1, reject_1); }); - } + myModule = new Promise((resolve_1, reject_1) => { require(["./0"], resolve_1, reject_1); }); method() { const loadAsync = new Promise((resolve_2, reject_2) => { require(["./0"], resolve_2, reject_2); }); this.myModule.then(Zero => { @@ -78,9 +76,7 @@ define(["require", "exports"], function (require, exports) { } } class D { - constructor() { - this.myModule = new Promise((resolve_4, reject_4) => { require(["./0"], resolve_4, reject_4); }); - } + myModule = new Promise((resolve_4, reject_4) => { require(["./0"], resolve_4, reject_4); }); method() { const loadAsync = new Promise((resolve_5, reject_5) => { require(["./0"], resolve_5, reject_5); }); this.myModule.then(Zero => { diff --git a/tests/baselines/reference/importCallExpressionInCJS5.js b/tests/baselines/reference/importCallExpressionInCJS5.js index b32b0e52c50f7..4e53f73ce1237 100644 --- a/tests/baselines/reference/importCallExpressionInCJS5.js +++ b/tests/baselines/reference/importCallExpressionInCJS5.js @@ -58,9 +58,7 @@ exports.backup = backup; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class C { - constructor() { - this.myModule = Promise.resolve().then(() => require("./0")); - } + myModule = Promise.resolve().then(() => require("./0")); method() { const loadAsync = Promise.resolve().then(() => require("./0")); this.myModule.then(Zero => { @@ -73,9 +71,7 @@ class C { } } class D { - constructor() { - this.myModule = Promise.resolve().then(() => require("./0")); - } + myModule = Promise.resolve().then(() => require("./0")); method() { const loadAsync = Promise.resolve().then(() => require("./0")); this.myModule.then(Zero => { diff --git a/tests/baselines/reference/importCallExpressionInSystem4.js b/tests/baselines/reference/importCallExpressionInSystem4.js index 55af57174bdf3..7f9430bbc2962 100644 --- a/tests/baselines/reference/importCallExpressionInSystem4.js +++ b/tests/baselines/reference/importCallExpressionInSystem4.js @@ -78,9 +78,7 @@ System.register([], function (exports_1, context_1) { setters: [], execute: function () { C = class C { - constructor() { - this.myModule = context_1.import("./0"); - } + myModule = context_1.import("./0"); method() { const loadAsync = context_1.import("./0"); this.myModule.then(Zero => { @@ -93,9 +91,7 @@ System.register([], function (exports_1, context_1) { } }; D = class D { - constructor() { - this.myModule = context_1.import("./0"); - } + myModule = context_1.import("./0"); method() { const loadAsync = context_1.import("./0"); this.myModule.then(Zero => { diff --git a/tests/baselines/reference/importCallExpressionInUMD4.js b/tests/baselines/reference/importCallExpressionInUMD4.js index 70a574f03028e..9ecfa77eb45d9 100644 --- a/tests/baselines/reference/importCallExpressionInUMD4.js +++ b/tests/baselines/reference/importCallExpressionInUMD4.js @@ -88,9 +88,7 @@ export class D { var __syncRequire = typeof module === "object" && typeof module.exports === "object"; Object.defineProperty(exports, "__esModule", { value: true }); class C { - constructor() { - this.myModule = __syncRequire ? Promise.resolve().then(() => require("./0")) : new Promise((resolve_1, reject_1) => { require(["./0"], resolve_1, reject_1); }); - } + myModule = __syncRequire ? Promise.resolve().then(() => require("./0")) : new Promise((resolve_1, reject_1) => { require(["./0"], resolve_1, reject_1); }); method() { const loadAsync = __syncRequire ? Promise.resolve().then(() => require("./0")) : new Promise((resolve_2, reject_2) => { require(["./0"], resolve_2, reject_2); }); this.myModule.then(Zero => { @@ -103,9 +101,7 @@ export class D { } } class D { - constructor() { - this.myModule = __syncRequire ? Promise.resolve().then(() => require("./0")) : new Promise((resolve_4, reject_4) => { require(["./0"], resolve_4, reject_4); }); - } + myModule = __syncRequire ? Promise.resolve().then(() => require("./0")) : new Promise((resolve_4, reject_4) => { require(["./0"], resolve_4, reject_4); }); method() { const loadAsync = __syncRequire ? Promise.resolve().then(() => require("./0")) : new Promise((resolve_5, reject_5) => { require(["./0"], resolve_5, reject_5); }); this.myModule.then(Zero => { diff --git a/tests/baselines/reference/potentiallyUncalledDecorators.js b/tests/baselines/reference/potentiallyUncalledDecorators.js index b69f848a8c933..eb099ba9d5f0c 100644 --- a/tests/baselines/reference/potentiallyUncalledDecorators.js +++ b/tests/baselines/reference/potentiallyUncalledDecorators.js @@ -86,16 +86,20 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, return c > 3 && r && Object.defineProperty(target, key, r), r; }; class FooComponent { + foo; } __decorate([ Input ], FooComponent.prototype, "foo", void 0); class Person { + person; + any; } __decorate([ tracked ], Person.prototype, "person", void 0); class MultiplyByTwo { + args; get multiplied() { return this.args.number * 2; } @@ -104,6 +108,7 @@ __decorate([ tracked('args') ], MultiplyByTwo.prototype, "multiplied", null); let A = class A { + foo; bar() { } }; __decorate([ @@ -116,6 +121,7 @@ A = __decorate([ noArgs ], A); let B = class B { + foo; bar() { } }; __decorate([ @@ -128,6 +134,7 @@ B = __decorate([ allRest ], B); let C = class C { + foo; bar() { } }; __decorate([ @@ -140,6 +147,7 @@ C = __decorate([ oneOptional ], C); let D = class D { + foo; bar() { } }; __decorate([ @@ -152,6 +160,7 @@ D = __decorate([ twoOptional ], D); let E = class E { + foo; bar() { } }; __decorate([ @@ -164,6 +173,7 @@ E = __decorate([ threeOptional ], E); let F = class F { + foo; bar() { } }; __decorate([ @@ -176,6 +186,7 @@ F = __decorate([ oneOptionalWithRest ], F); let G = class G { + foo; bar() { } }; __decorate([ diff --git a/tests/baselines/reference/spreadMethods.js b/tests/baselines/reference/spreadMethods.js index b35619361f1f0..e49bc557961b7 100644 --- a/tests/baselines/reference/spreadMethods.js +++ b/tests/baselines/reference/spreadMethods.js @@ -43,9 +43,7 @@ sso.g; // ok //// [spreadMethods.js] class K { - constructor() { - this.p = 12; - } + p = 12; m() { } get g() { return 0; } } diff --git a/tests/baselines/reference/systemModuleTargetES6.js b/tests/baselines/reference/systemModuleTargetES6.js index 943e57ff742ac..0df2835683e82 100644 --- a/tests/baselines/reference/systemModuleTargetES6.js +++ b/tests/baselines/reference/systemModuleTargetES6.js @@ -35,8 +35,8 @@ System.register([], function (exports_1, context_1) { MyClass2 = class MyClass2 { static getInstance() { return MyClass2.value; } }; - MyClass2.value = 42; exports_1("MyClass2", MyClass2); + MyClass2.value = 42; } }; }); diff --git a/tests/baselines/reference/taggedTemplatesWithTypeArguments2.js b/tests/baselines/reference/taggedTemplatesWithTypeArguments2.js index bc170467fa8be..84817451a6d79 100644 --- a/tests/baselines/reference/taggedTemplatesWithTypeArguments2.js +++ b/tests/baselines/reference/taggedTemplatesWithTypeArguments2.js @@ -53,6 +53,9 @@ const d = (new tag `${"hello"} ${"world"}`)(100, 200); */ const e = new tag `hello`(); class SomeBase { + a; + b; + c; } class SomeDerived extends SomeBase { constructor() { diff --git a/tests/baselines/reference/transformApi/transformsCorrectly.rewrittenNamespaceFollowingClass.js b/tests/baselines/reference/transformApi/transformsCorrectly.rewrittenNamespaceFollowingClass.js index 3f2dd6cdb56fb..0698fbf19d14e 100644 --- a/tests/baselines/reference/transformApi/transformsCorrectly.rewrittenNamespaceFollowingClass.js +++ b/tests/baselines/reference/transformApi/transformsCorrectly.rewrittenNamespaceFollowingClass.js @@ -1,9 +1,7 @@ class C { - constructor() { - this.foo = 10; - } + foo = 10; + static bar = 20; } -C.bar = 20; (function (C) { C.x = 10; })(C || (C = {})); diff --git a/tests/baselines/reference/uniqueSymbols.js b/tests/baselines/reference/uniqueSymbols.js index 0e6bde4fe474c..62a8a3ab69e05 100644 --- a/tests/baselines/reference/uniqueSymbols.js +++ b/tests/baselines/reference/uniqueSymbols.js @@ -308,14 +308,13 @@ async function* asyncGenFuncYieldLetCall() { yield letCall; } async function* asyncGenFuncYieldVarCall() { yield varCall; } // classes class C { - constructor() { - this.readonlyCall = Symbol(); - this.readwriteCall = Symbol(); - } + static readonlyStaticCall = Symbol(); + static readonlyStaticType; + static readonlyStaticTypeAndCall = Symbol(); + static readwriteStaticCall = Symbol(); + readonlyCall = Symbol(); + readwriteCall = Symbol(); } -C.readonlyStaticCall = Symbol(); -C.readonlyStaticTypeAndCall = Symbol(); -C.readwriteStaticCall = Symbol(); const constInitToCReadonlyStaticCall = C.readonlyStaticCall; const constInitToCReadonlyStaticType = C.readonlyStaticType; const constInitToCReadonlyStaticTypeAndCall = C.readonlyStaticTypeAndCall; @@ -364,26 +363,24 @@ const o2 = { }; // property initializers class C0 { - constructor() { - this.a = s; - this.b = N.s; - this.c = N["s"]; - this.d = s; - this.e = N.s; - this.f = N["s"]; - } + static a = s; + static b = N.s; + static c = N["s"]; + static d = s; + static e = N.s; + static f = N["s"]; + a = s; + b = N.s; + c = N["s"]; + d = s; + e = N.s; + f = N["s"]; method1() { return s; } async method2() { return s; } async *method3() { yield s; } *method4() { yield s; } method5(p = s) { return p; } } -C0.a = s; -C0.b = N.s; -C0.c = N["s"]; -C0.d = s; -C0.e = N.s; -C0.f = N["s"]; // non-widening positions // element access o[s]; @@ -410,8 +407,11 @@ Math.random() * 2 ? N["s"] : "a"; [N.s]: "b", }); class C1 { + static [s]; + static [N.s]; + [s]; + [N.s]; } -N.s, N.s; const o3 = { method1() { return s; // return type should not widen due to contextual type diff --git a/tests/baselines/reference/uniqueSymbolsDeclarations.js b/tests/baselines/reference/uniqueSymbolsDeclarations.js index 02a692aa9ff63..af3b56f106da4 100644 --- a/tests/baselines/reference/uniqueSymbolsDeclarations.js +++ b/tests/baselines/reference/uniqueSymbolsDeclarations.js @@ -283,14 +283,13 @@ async function* asyncGenFuncYieldLetCall() { yield letCall; } async function* asyncGenFuncYieldVarCall() { yield varCall; } // classes class C { - constructor() { - this.readonlyCall = Symbol(); - this.readwriteCall = Symbol(); - } + static readonlyStaticCall = Symbol(); + static readonlyStaticType; + static readonlyStaticTypeAndCall = Symbol(); + static readwriteStaticCall = Symbol(); + readonlyCall = Symbol(); + readwriteCall = Symbol(); } -C.readonlyStaticCall = Symbol(); -C.readonlyStaticTypeAndCall = Symbol(); -C.readwriteStaticCall = Symbol(); const constInitToCReadonlyStaticCall = C.readonlyStaticCall; const constInitToCReadonlyStaticType = C.readonlyStaticType; const constInitToCReadonlyStaticTypeAndCall = C.readonlyStaticTypeAndCall; @@ -339,26 +338,24 @@ const o2 = { }; // property initializers class C0 { - constructor() { - this.a = s; - this.b = N.s; - this.c = N["s"]; - this.d = s; - this.e = N.s; - this.f = N["s"]; - } + static a = s; + static b = N.s; + static c = N["s"]; + static d = s; + static e = N.s; + static f = N["s"]; + a = s; + b = N.s; + c = N["s"]; + d = s; + e = N.s; + f = N["s"]; method1() { return s; } async method2() { return s; } async *method3() { yield s; } *method4() { yield s; } method5(p = s) { return p; } } -C0.a = s; -C0.b = N.s; -C0.c = N["s"]; -C0.d = s; -C0.e = N.s; -C0.f = N["s"]; // non-widening positions // element access o[s]; @@ -385,8 +382,11 @@ Math.random() * 2 ? N["s"] : "a"; [N.s]: "b", }); class C1 { + static [s]; + static [N.s]; + [s]; + [N.s]; } -N.s, N.s; const o4 = { method1() { return s; // return type should not widen due to contextual type diff --git a/tests/baselines/reference/uniqueSymbolsDeclarationsErrors.js b/tests/baselines/reference/uniqueSymbolsDeclarationsErrors.js index 871d8977a9de2..46227ddf8e782 100644 --- a/tests/baselines/reference/uniqueSymbolsDeclarationsErrors.js +++ b/tests/baselines/reference/uniqueSymbolsDeclarationsErrors.js @@ -84,6 +84,8 @@ function funcInferredReturnType(obj) { } exports.funcInferredReturnType = funcInferredReturnType; class ClassWithPrivateNamedProperties { + [s]; + static [s]; } exports.ClassWithPrivateNamedProperties = ClassWithPrivateNamedProperties; class ClassWithPrivateNamedMethods { diff --git a/tests/baselines/reference/uniqueSymbolsErrors.js b/tests/baselines/reference/uniqueSymbolsErrors.js index ad3c5c2006538..ed17e52719a0e 100644 --- a/tests/baselines/reference/uniqueSymbolsErrors.js +++ b/tests/baselines/reference/uniqueSymbolsErrors.js @@ -91,6 +91,8 @@ const shouldNotBeAssignable: string = Symbol(); // classes class InvalidClass { constructor(invalidConstructorArgType) { } + invalidReadonlyPropertyType; + invalidPropertyType; invalidArgType(arg) { return; } invalidRestArgType(...args) { return; } invalidReturnType() { return; } @@ -100,6 +102,7 @@ class InvalidClass { invalidTypeParameterDefault() { return; } get invalidGetter() { return; } set invalidSetter(arg) { return; } + static invalidStaticPropertyType; static invalidStaticArgType(arg) { return; } static invalidStaticRestArgType(...args) { return; } static invalidStaticReturnType() { return; } diff --git a/tests/baselines/reference/variableDeclarationDeclarationEmitUniqueSymbolPartialStatement.js b/tests/baselines/reference/variableDeclarationDeclarationEmitUniqueSymbolPartialStatement.js index f2a1df5e7072a..4bb3c19f9c890 100644 --- a/tests/baselines/reference/variableDeclarationDeclarationEmitUniqueSymbolPartialStatement.js +++ b/tests/baselines/reference/variableDeclarationDeclarationEmitUniqueSymbolPartialStatement.js @@ -16,8 +16,8 @@ var Foo = /** @class */ (function () { } return Foo; }()); -_a = key; exports.Foo = Foo; +_a = key; //// [variableDeclarationDeclarationEmitUniqueSymbolPartialStatement.d.ts]