Repository: nestjs/swagger
Pull request #572 (2020-02-19 19:33:00)

chore(deps): update dependency @types/jest to v25.1.3
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [@types/jest](https://togithub.com/DefinitelyTyped/DefinitelyTyped) | devDependencies | patch | [`25.1.2` -> `25.1.3`](https://renovatebot.com/diffs/npm/@types%2fjest/25.1.2/25.1.3) |

---

### Renovate configuration

:date: **Schedule**: At any time (no schedule defined).

:vertical_traffic_light: **Automerge**: Enabled.

:recycle: **Rebasing**: Whenever PR becomes conflicted, or if you tick the rebase/retry checkbox below.

:no_bell: **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#nestjs/swagger).


Patch:
diff --git a/.npmignore b/.npmignore
index 1a3a35c6..ff40750d 100644
--- a/.npmignore
+++ b/.npmignore
@@ -18,3 +18,6 @@ tsconfig.json
 renovate.json
 npm-debug.log
 .DS_Store
+.commitlintrc.json
+.release-it.json
+CONTRIBUTING.md
diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts
index 15db767c..b978142c 100644
--- a/lib/plugin/utils/plugin-utils.ts
+++ b/lib/plugin/utils/plugin-utils.ts
@@ -30,7 +30,7 @@ export function getTypeReferenceAsString(
     const arrayType = getTypeArguments(type)[0];
     const elementType = getTypeReferenceAsString(arrayType, typeChecker);
     if (!elementType) {
-      return `undefined`;
+      return undefined;
     }
     return `[${elementType}]`;
   }
@@ -62,7 +62,10 @@ export function getTypeReferenceAsString(
     if (text === Date.name) {
       return text;
     }
-    if (isAutoGeneratedUnion(type, typeChecker)) {
+    if (
+      isAutoGeneratedTypeUnion(type) ||
+      isAutoGeneratedEnumUnion(type, typeChecker)
+    ) {
       const types = (type as ts.UnionOrIntersectionType).types;
       return getTypeReferenceAsString(types[types.length - 1], typeChecker);
     }
@@ -144,21 +147,24 @@ export function isDynamicallyAdded(identifier: ts.Node) {
 }
 
 /**
- * when "strict" mode enabled, TypeScript transform the type signature of optional properties to
- * the {undefined | T} where T is the original type. Hence, we have to extract the last type of type union
+ * when "strict" mode enabled, TypeScript transform the enum type to a union composed of
+ * the enum values and the undefined type. Hence, we have to lookup all the union types to get the original type
  * @param type
  */
-export function isAutoGeneratedUnion(
+export function isAutoGeneratedEnumUnion(
   type: ts.Type,
   typeChecker: ts.TypeChecker
-): boolean | ts.Type {
+): ts.Type {
   if (type.isUnionOrIntersection() && !isEnum(type)) {
     if (!type.types) {
-      return false;
+      return undefined;
     }
     const undefinedTypeIndex = type.types.findIndex(
       (type: any) => type.intrinsicName === 'undefined'
     );
+    if (undefinedTypeIndex < 0) {
+      return undefined;
+    }
 
     // "strict" mode for enums
     let parentType = undefined;
@@ -169,7 +175,10 @@ export function isAutoGeneratedUnion(
       if (!item.symbol) {
         return false;
       }
-      if (!(item.symbol as any).parent) {
+      if (
+        !(item.symbol as any).parent ||
+        item.symbol.flags !== ts.SymbolFlags.EnumMember
+      ) {
         return false;
       }
       const symbolType = typeChecker.getDeclaredTypeOfSymbol(
@@ -184,6 +193,24 @@ export function isAutoGeneratedUnion(
     if (isParentSymbolEqual) {
       return parentType;
     }
+  }
+  return undefined;
+}
+
+/**
+ * when "strict" mode enabled, TypeScript transform the type signature of optional properties to
+ * the {undefined | T} where T is the original type. Hence, we have to extract the last type of type union
+ * @param type
+ */
+export function isAutoGeneratedTypeUnion(type: ts.Type): boolean {
+  if (type.isUnionOrIntersection() && !isEnum(type)) {
+    if (!type.types) {
+      return false;
+    }
+    const undefinedTypeIndex = type.types.findIndex(
+      (type: any) => type.intrinsicName === 'undefined'
+    );
+
     // "strict" mode for non-enum properties
     if (type.types.length === 2 && undefinedTypeIndex >= 0) {
       return true;
@@ -191,3 +218,20 @@ export function isAutoGeneratedUnion(
   }
   return false;
 }
+
+export function extractTypeArgumentIfArray(type: ts.Type) {
+  if (isArray(type)) {
+    type = getTypeArguments(type)[0];
+    if (!type) {
+      return undefined;
+    }
+    return {
+      type,
+      isArray: true
+    };
+  }
+  return {
+    type,
+    isArray: false
+  };
+}
diff --git a/lib/plugin/visitors/model-class.visitor.ts b/lib/plugin/visitors/model-class.visitor.ts
index 0466bfc8..c1b85f0b 100644
--- a/lib/plugin/visitors/model-class.visitor.ts
+++ b/lib/plugin/visitors/model-class.visitor.ts
@@ -3,18 +3,14 @@ import * as ts from 'typescript';
 import { ApiHideProperty } from '../../decorators';
 import { PluginOptions } from '../merge-options';
 import { METADATA_FACTORY_NAME } from '../plugin-constants';
+import { getDecoratorArguments, getText, isEnum } from '../utils/ast-utils';
 import {
-  getDecoratorArguments,
-  getText,
-  getTypeArguments,
-  isArray,
-  isEnum
-} from '../utils/ast-utils';
-import {
+  extractTypeArgumentIfArray,
   getDecoratorOrUndefinedByNames,
   getTypeReferenceAsString,
   hasPropertyKey,
-  isAutoGeneratedUnion,
+  isAutoGeneratedEnumUnion,
+  isAutoGeneratedTypeUnion,
   replaceImportPath
 } from '../utils/plugin-utils';
 import { AbstractFileVisitor } from './abstract.visitor';
@@ -229,21 +225,28 @@ export class ModelClassVisitor extends AbstractFileVisitor {
     if (!type) {
       return undefined;
     }
-    let isArrayType = false;
-    if (isArray(type)) {
-      type = getTypeArguments(type)[0];
-      isArrayType = true;
+    if (isAutoGeneratedTypeUnion(type)) {
+      const types = (type as ts.UnionOrIntersectionType).types;
+      type = types[types.length - 1];
+    }
+    const typeIsArrayTuple = extractTypeArgumentIfArray(type);
+    if (!typeIsArrayTuple) {
+      return undefined;
+    }
+    let isArrayType = typeIsArrayTuple.isArray;
+    type = typeIsArrayTuple.type;
+
+    if (!isEnum(type)) {
+      type = isAutoGeneratedEnumUnion(type, typeChecker);
       if (!type) {
         return undefined;
       }
-    }
-    if (!isEnum(type)) {
-      const typeOrBool = isAutoGeneratedUnion(type, typeChecker);
-      if (typeof typeOrBool !== 'boolean') {
-        type = typeOrBool;
-      } else {
+      const typeIsArrayTuple = extractTypeArgumentIfArray(type);
+      if (!typeIsArrayTuple) {
         return undefined;
       }
+      isArrayType = typeIsArrayTuple.isArray;
+      type = typeIsArrayTuple.type;
     }
     const enumRef = replaceImportPath(getText(type, typeChecker), hostFilename);
     const enumProperty = ts.createPropertyAssignment(
diff --git a/package-lock.json b/package-lock.json
index 962ad956..f0975ea8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/swagger",
-  "version": "4.2.10",
+  "version": "4.2.11",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1649,9 +1649,9 @@
       }
     },
     "@types/jest": {
-      "version": "25.1.2",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.2.tgz",
-      "integrity": "sha512-EsPIgEsonlXmYV7GzUqcvORsSS9Gqxw/OvkGwHfAdpjduNRxMlhsav0O5Kb0zijc/eXSO/uW6SJt9nwull8AUQ==",
+      "version": "25.1.3",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.3.tgz",
+      "integrity": "sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg==",
       "dev": true,
       "requires": {
         "jest-diff": "^25.1.0",
diff --git a/package.json b/package.json
index 315f4729..49cd4854 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nestjs/swagger",
-  "version": "4.2.10",
+  "version": "4.2.11",
   "description": "Nest - modern, fast, powerful node.js web framework (@swagger)",
   "author": "Kamil Mysliwiec",
   "license": "MIT",
@@ -29,7 +29,7 @@
     "@nestjs/core": "6.11.7",
     "@nestjs/platform-express": "6.11.7",
     "@nestjs/platform-fastify": "6.11.7",
-    "@types/jest": "25.1.2",
+    "@types/jest": "25.1.3",
     "@types/lodash": "4.14.149",
     "@types/node": "11.15.0",
     "express": "4.17.1",


Test patch:
diff --git a/test/plugin/fixtures/create-cat.dto.ts b/test/plugin/fixtures/create-cat.dto.ts
index 0a3359d2..4714a56d 100644
--- a/test/plugin/fixtures/create-cat.dto.ts
+++ b/test/plugin/fixtures/create-cat.dto.ts
@@ -18,6 +18,7 @@ export class CreateCatDto {
   tags: string[];
   status: Status = Status.ENABLED;
   status2?: Status;
+  statusArr?: Status[];
 
   @ApiProperty({ type: String })
   @IsString()
@@ -46,7 +47,7 @@ export class CreateCatDto {
         this.status = Status.ENABLED;
     }
     static _OPENAPI_METADATA_FACTORY() {
-        return { name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, status2: { required: false, enum: Status }, breed: { required: false, type: () => String }, nodes: { required: true, type: () => [Object] }, date: { required: true, type: () => Date } };
+        return { name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, 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 }, breed: { required: false, type: () => String }, nodes: { required: true, type: () => [Object] }, date: { required: true, type: () => Date } };
     }
 }
 __decorate([