Repository: nestjs/swagger
Pull request #3324 (2025-02-27 17:47:42)

feat: support native private class properties in model class visitor
## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->
- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: [!3322](https://github.com/nestjs/swagger/issues/3322)


## What is the new behavior?
Private properties of a class are omitted now, so they do not cause the `SyntaxError`.

## Does this PR introduce a breaking change?
- [ ] Yes
- [x] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information



Patch:
diff --git a/lib/plugin/visitors/model-class.visitor.ts b/lib/plugin/visitors/model-class.visitor.ts
index d2a17d92..04c24813 100644
--- a/lib/plugin/visitors/model-class.visitor.ts
+++ b/lib/plugin/visitors/model-class.visitor.ts
@@ -167,6 +167,11 @@ export class ModelClassVisitor extends AbstractFileVisitor {
       return node;
     }
 
+    const isPrivateProperty = ts.isPrivateIdentifier(node.name);
+    if (isPrivateProperty) {
+      return node;
+    }
+
     const decorators = ts.canHaveDecorators(node) && ts.getDecorators(node);
 
     const classTransformerShim = options.classTransformerShim;


Test patch:
diff --git a/test/plugin/fixtures/create-cat-alt.dto.ts b/test/plugin/fixtures/create-cat-alt.dto.ts
index 8161827f..2d846b59 100644
--- a/test/plugin/fixtures/create-cat-alt.dto.ts
+++ b/test/plugin/fixtures/create-cat-alt.dto.ts
@@ -42,10 +42,12 @@ export class CreateCatDto2 {
     [x: string]: string;
   }
   amount: bigint;
+  #privateProperty: string;
 }
 `;
 
-export const createCatDtoTextAltTranspiled = `import * as openapi from "@nestjs/swagger";
+export const createCatDtoTextAltTranspiled = `var _CreateCatDto2_privateProperty;
+import * as openapi from "@nestjs/swagger";
 import * as package from 'class-validator';
 var Status;
 (function (Status) {
@@ -56,11 +58,13 @@ export class CreateCatDto2 {
     constructor() {
         this.age = 3;
         this.status = Status.ENABLED;
+        _CreateCatDto2_privateProperty.set(this, void 0);
     }
     static _OPENAPI_METADATA_FACTORY() {
         return { name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, breed: { required: false, type: () => String }, nodes: { required: true, type: () => [Object] }, alias: { required: true, type: () => Object }, numberAlias: { required: true, type: () => Number, description: "NumberAlias" }, union: { required: true, type: () => Object }, intersection: { required: true, type: () => Object }, nested: { required: true, type: () => ({ first: { required: true, type: () => String }, second: { required: true, type: () => Number }, status: { required: true, enum: Status }, tags: { required: true, type: () => [String] }, nodes: { required: true, type: () => [Object] }, alias: { required: true, type: () => Object }, numberAlias: { required: true, type: () => Number } }) }, amount: { required: true, type: () => BigInt } };
     }
 }
+_CreateCatDto2_privateProperty = new WeakMap();
 __decorate([
     package.IsString()
 ], CreateCatDto2.prototype, "name", void 0);
diff --git a/test/plugin/fixtures/create-cat-alt2.dto.ts b/test/plugin/fixtures/create-cat-alt2.dto.ts
index 586085ed..63594d78 100644
--- a/test/plugin/fixtures/create-cat-alt2.dto.ts
+++ b/test/plugin/fixtures/create-cat-alt2.dto.ts
@@ -81,16 +81,31 @@ export abstract class Audit {
    * @example 5
    */
   testNumber: number;
+
+  /**
+   * privateProperty
+   * @example 'secret'
+   */
+  #privateProperty: string;
 }
 `;
 
-export const createCatDtoTextAlt2Transpiled = `import * as openapi from "@nestjs/swagger";
+export const createCatDtoTextAlt2Transpiled = `var _Audit_privateProperty;
+import * as openapi from "@nestjs/swagger";
 import { CreateDateColumn, UpdateDateColumn, VersionColumn } from 'typeorm';
 export class Audit {
+    constructor() {
+        /**
+         * privateProperty
+         * @example 'secret'
+         */
+        _Audit_privateProperty.set(this, void 0);
+    }
     static _OPENAPI_METADATA_FACTORY() {
         return { createdAt: { required: true, type: () => Object, description: "test on createdAt" }, updatedAt: { required: true, type: () => Object }, version: { required: true, type: () => String, description: "test\\nversion", example: "version 123" }, testVersion: { required: true, type: () => Object, description: "testVersion", examples: ["0.0.1", "0.0.2"], deprecated: true }, testVersion2: { required: true, type: () => Object, description: "testVersion2", examples: ["0.0.1", "0.0.2"], deprecated: true }, testVersionArray: { required: true, type: () => [String], description: "testVersionArray", example: ["0.0.1", "0.0.2"] }, testVersionArray2: { required: true, type: () => [String], description: "testVersionArray", example: ["version 123", "version 321"] }, testVersionArray3: { required: true, type: () => [Number], description: "testVersionArray", example: [123, 321] }, testBoolean: { required: true, type: () => Boolean, description: "testBoolean", example: true }, testNumber: { required: true, type: () => Number, description: "testNumber", examples: [1, 5] } };
     }
 }
+_Audit_privateProperty = new WeakMap();
 __decorate([
     CreateDateColumn()
 ], Audit.prototype, "createdAt", void 0);
diff --git a/test/plugin/fixtures/create-cat.dto.ts b/test/plugin/fixtures/create-cat.dto.ts
index cb645140..700faeec 100644
--- a/test/plugin/fixtures/create-cat.dto.ts
+++ b/test/plugin/fixtures/create-cat.dto.ts
@@ -95,10 +95,13 @@ export class CreateCatDto {
   cryptoUUIDProperty: UUID;
 
   arrayOfUUIDs: UUID[];
+
+  #privateProperty: string;
 }
 `;
 
-export const createCatDtoTextTranspiled = `import * as openapi from "@nestjs/swagger";
+export const createCatDtoTextTranspiled = `var _CreateCatDto_privateProperty;
+import * as openapi from "@nestjs/swagger";
 import { IsString, IsPositive, IsNegative, Length, Matches, IsIn } from 'class-validator';
 var Status;
 (function (Status) {
@@ -120,11 +123,13 @@ export class CreateCatDto {
         this.positive = 5;
         this.negative = -1;
         this.status = Status.ENABLED;
+        _CreateCatDto_privateProperty.set(this, void 0);
     }
     static _OPENAPI_METADATA_FACTORY() {
         return { isIn: { required: true, type: () => String, enum: ['a', 'b'] }, pattern: { required: true, type: () => String, pattern: "/^[+]?abc$/" }, name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, positive: { required: true, type: () => Number, default: 5, minimum: 1 }, negative: { required: true, type: () => Number, default: -1, maximum: -1 }, lengthMin: { required: true, type: () => String, minLength: 2 }, lengthMinMax: { required: true, type: () => String, minLength: 3, maxLength: 5 }, names: { required: true, type: () => [String], minItems: 1, uniqueItems: true, maxItems: 10 }, employees: { required: true, type: () => [String], minItems: 1 }, nominator: { required: true, type: () => String, multipleOf: 2 }, encodedInfo: { required: true, type: () => String, format: "base64" }, creditCard: { required: true, type: () => String, format: "credit-card" }, currency: { required: true, type: () => String, format: "currency" }, email: { required: true, type: () => String, format: "email" }, response: { required: true, type: () => Object, format: "json" }, githubAccount: { required: true, type: () => String, format: "uri" }, transactionId: { required: true, type: () => String, format: "uuid" }, phoneNumber: { required: true, type: () => String, format: "mobile-phone" }, char: { required: true, type: () => String, pattern: "^[\\\\x00-\\\\x7F]+$" }, color: { required: true, type: () => String, pattern: "^#?([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$" }, hex: { required: true, type: () => String, pattern: "^(0x|0h)?[0-9A-F]+$" }, searchBy: { required: true, type: () => String, pattern: "log_" }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, status2: { required: false, enum: Status }, statusArr: { required: false, enum: Status, isArray: true }, oneValueEnum: { required: false, enum: OneValueEnum }, oneValueEnumArr: { required: false, enum: OneValueEnum, isArray: true }, breed: { required: false, type: () => String, title: "this is breed im comment" }, nodes: { required: true, type: () => [Object] }, optionalBoolean: { required: false, type: () => Boolean }, date: { required: true, type: () => Date }, twoDimensionPrimitives: { required: true, type: () => [[String]] }, twoDimensionNodes: { required: true, type: () => [[OtherNode]] }, cryptoUUIDProperty: { required: true, type: () => String }, arrayOfUUIDs: { required: true, type: () => [String] } };
     }
 }
+_CreateCatDto_privateProperty = new WeakMap();
 __decorate([
     IsIn(['a', 'b'])
 ], CreateCatDto.prototype, \"isIn\", void 0);