diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index f1dc7fcf6b9..935c549dbce 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -923,7 +923,7 @@ namespace ts { function getClassFacts(node: ClassLikeDeclaration) { let facts = ClassFacts.None; const original = getOriginalNode(node); - if (isClassDeclaration(original) && some(original.decorators)) { + if (isClassDeclaration(original) && classOrConstructorParameterIsDecorated(original)) { facts |= ClassFacts.ClassWasDecorated; } for (const member of node.members) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index aa677c911b8..78eb34adac8 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -567,35 +567,12 @@ namespace ts { visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, alwaysStrict)); } - /** - * Tests whether we should emit a __decorate call for a class declaration. - */ - function shouldEmitDecorateCallForClass(node: ClassDeclaration) { - if (node.decorators && node.decorators.length > 0) { - return true; - } - - const constructor = getFirstConstructorWithBody(node); - if (constructor) { - return forEach(constructor.parameters, shouldEmitDecorateCallForParameter); - } - - return false; - } - - /** - * Tests whether we should emit a __decorate call for a parameter declaration. - */ - function shouldEmitDecorateCallForParameter(parameter: ParameterDeclaration) { - return parameter.decorators !== undefined && parameter.decorators.length > 0; - } - function getClassFacts(node: ClassDeclaration, staticProperties: readonly PropertyDeclaration[]) { let facts = ClassFacts.None; if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties; const extendsClauseElement = getEffectiveBaseTypeNode(node); if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass; - if (shouldEmitDecorateCallForClass(node)) facts |= ClassFacts.HasConstructorDecorators; + if (classOrConstructorParameterIsDecorated(node)) facts |= ClassFacts.HasConstructorDecorators; if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators; if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace; else if (isDefaultExternalModuleExport(node)) facts |= ClassFacts.IsDefaultExternalExport; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 482bc94c150..05d2194abd6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1868,12 +1868,19 @@ namespace ts { return some((node as ClassDeclaration).members, m => nodeOrChildIsDecorated(m, node, parent!)); // TODO: GH#18217 case SyntaxKind.MethodDeclaration: case SyntaxKind.SetAccessor: + case SyntaxKind.Constructor: return some((node as FunctionLikeDeclaration).parameters, p => nodeIsDecorated(p, node, parent!)); // TODO: GH#18217 default: return false; } } + export function classOrConstructorParameterIsDecorated(node: ClassDeclaration): boolean { + if (nodeIsDecorated(node)) return true; + const constructor = getFirstConstructorWithBody(node); + return !!constructor && childIsDecorated(constructor, node); + } + export function isJSXTagName(node: Node) { const { parent } = node; if (parent.kind === SyntaxKind.JsxOpeningElement || diff --git a/src/tsserver/nodeServer.ts b/src/tsserver/nodeServer.ts index 1cbce2091e1..e8e42042d55 100644 --- a/src/tsserver/nodeServer.ts +++ b/src/tsserver/nodeServer.ts @@ -256,7 +256,7 @@ namespace ts.server { /* eslint-enable no-restricted-globals */ if (typeof global !== "undefined" && global.gc) { - sys.gc = () => global.gc!(); + sys.gc = () => global.gc?.(); } sys.require = (initialDir: string, moduleName: string): RequireResult => { diff --git a/tests/baselines/reference/decoratorOnClassConstructorParameter5.js b/tests/baselines/reference/decoratorOnClassConstructorParameter5.js new file mode 100644 index 00000000000..3fd7291bb7e --- /dev/null +++ b/tests/baselines/reference/decoratorOnClassConstructorParameter5.js @@ -0,0 +1,23 @@ +//// [decoratorOnClassConstructorParameter5.ts] +// https://github.com/microsoft/TypeScript/issues/44931 +interface IFoo { } +declare const IFoo: any; +class BulkEditPreviewProvider { + static readonly Schema = 'vscode-bulkeditpreview'; + static emptyPreview = { scheme: BulkEditPreviewProvider.Schema }; + constructor( + @IFoo private readonly _modeService: IFoo, + ) { } +} + +//// [decoratorOnClassConstructorParameter5.js] +let BulkEditPreviewProvider = class BulkEditPreviewProvider { + constructor(_modeService) { + this._modeService = _modeService; + } +}; +BulkEditPreviewProvider.Schema = 'vscode-bulkeditpreview'; +BulkEditPreviewProvider.emptyPreview = { scheme: BulkEditPreviewProvider.Schema }; +BulkEditPreviewProvider = __decorate([ + __param(0, IFoo) +], BulkEditPreviewProvider); diff --git a/tests/cases/conformance/decorators/class/constructor/parameter/decoratorOnClassConstructorParameter5.ts b/tests/cases/conformance/decorators/class/constructor/parameter/decoratorOnClassConstructorParameter5.ts new file mode 100644 index 00000000000..36790478959 --- /dev/null +++ b/tests/cases/conformance/decorators/class/constructor/parameter/decoratorOnClassConstructorParameter5.ts @@ -0,0 +1,15 @@ +// @target: es2018 +// @experimentalDecorators: true +// @noEmitHelpers: true +// @noTypesAndSymbols: true + +// https://github.com/microsoft/TypeScript/issues/44931 +interface IFoo { } +declare const IFoo: any; +class BulkEditPreviewProvider { + static readonly Schema = 'vscode-bulkeditpreview'; + static emptyPreview = { scheme: BulkEditPreviewProvider.Schema }; + constructor( + @IFoo private readonly _modeService: IFoo, + ) { } +} \ No newline at end of file