Repository: nestjs/swagger (ts)

Issue:
Support "Uppercase<string>" and "Lowercase<string>" by treating them as normal strings
### Is there an existing issue that is already proposing this?

- [X] I have searched the existing issues

### Is your feature request related to a problem? Please describe it

Our API exchanges data between several systems. One of the differences between these systems is that one of them uses lowerstring-uuids and the other one uses upperstring-uuids. So we want to declare these IDs as "Uppercase<string>" or "Lowercase<string>" via typescript in there classes.

When using the Swagger-Plugin there recognized as "{}" and the [Swagger-Plugin for TS-Jest](https://docs.nestjs.com/openapi/cli-plugin#integration-with-ts-jest-e2e-tests) throws with `A circular dependency has been detected (property key: "id"). Please, make sure that each side of a bidirectional relationships are using lazy resolvers ("type: () => ClassType").`

### Describe the solution you'd like

It would be great if the Swagger plugin would treat "Uppercase<string>" and "Lowercase<string>" as normal strings.

### Teachability, documentation, adoption, migration strategy

_No response_

### What is the motivation / use case for changing the behavior?

Reduce boilerplate.

Currently:

```ts
import { ApiProperty } from '@nestjs/swagger';

export class CatDto {
  @ApiProperty({ type: String })
  id!: Uppercase<string>
}
```

With wished feature:

```ts
export class CatDto {
  id!: Uppercase<string>
}
```

Pull request description:
feat: add support for string mapping types
## 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. -->

String mappings like `Uppercase<string>` or `Lowercase<string>` are not recognized as string type.

Issue Number: #2606


## What is the new behavior?

String mappings are recognized as string type.

## 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

Closes #2606 

Patch:
diff --git a/lib/plugin/utils/ast-utils.ts b/lib/plugin/utils/ast-utils.ts
index 2078a1d2..162be365 100644
--- a/lib/plugin/utils/ast-utils.ts
+++ b/lib/plugin/utils/ast-utils.ts
@@ -46,6 +46,10 @@ export function isStringLiteral(type: Type) {
   return hasFlag(type, TypeFlags.StringLiteral) && !type.isUnion();
 }
 
+export function isStringMapping(type: Type) {
+  return hasFlag(type, TypeFlags.StringMapping);
+}
+
 export function isNumber(type: Type) {
   return hasFlag(type, TypeFlags.Number);
 }
diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts
index 3608931f..138a7f85 100644
--- a/lib/plugin/utils/plugin-utils.ts
+++ b/lib/plugin/utils/plugin-utils.ts
@@ -13,7 +13,8 @@ import {
   isInterface,
   isNumber,
   isString,
-  isStringLiteral
+  isStringLiteral,
+  isStringMapping
 } from './ast-utils';
 
 export function getDecoratorOrUndefinedByNames(
@@ -65,7 +66,7 @@ export function getTypeReferenceAsString(
   if (isBigInt(type)) {
     return { typeName: BigInt.name, arrayDepth };
   }
-  if (isString(type) || isStringLiteral(type)) {
+  if (isString(type) || isStringLiteral(type) || isStringMapping(type)) {
     return { typeName: String.name, arrayDepth };
   }
   if (isPromiseOrObservable(getText(type, typeChecker))) {


Test patch:
diff --git a/test/plugin/fixtures/project/cats/classes/cat.class.ts b/test/plugin/fixtures/project/cats/classes/cat.class.ts
index a70189eb..7a875ab4 100644
--- a/test/plugin/fixtures/project/cats/classes/cat.class.ts
+++ b/test/plugin/fixtures/project/cats/classes/cat.class.ts
@@ -25,4 +25,12 @@ export class Cat {
   enum: LettersEnum;
 
   enumArr: LettersEnum;
+
+  uppercaseString: Uppercase<string>;
+  
+  lowercaseString: Lowercase<string>;
+
+  capitalizeString: Capitalize<string>;
+
+  uncapitalizeString: Uncapitalize<string>;
 }
diff --git a/test/plugin/fixtures/serialized-meta.fixture.ts b/test/plugin/fixtures/serialized-meta.fixture.ts
index b87554c3..9bd46dec 100644
--- a/test/plugin/fixtures/serialized-meta.fixture.ts
+++ b/test/plugin/fixtures/serialized-meta.fixture.ts
@@ -64,7 +64,11 @@ export default async () => {
               enumArr: {
                 required: true,
                 enum: t['./cats/dto/pagination-query.dto'].LettersEnum
-              }
+              },
+              uppercaseString: { required: true, type: () => String },
+              lowercaseString: { required: true, type: () => String },
+              capitalizeString: { required: true, type: () => String },
+              uncapitalizeString: { required: true, type: () => String }
             }
           }
         ],