diff --git a/.gitignore b/.gitignore index 58a63eb872..3d5bfe3e55 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ packages/*/*.tgz packages/*/docs package-lock.json *.log -.env \ No newline at end of file +.env +# bun.lock +playground/ diff --git a/packages/core/src/transformers/MethodTransformer.ts b/packages/core/src/transformers/MethodTransformer.ts index 8f8c363b5f..7affff5a86 100644 --- a/packages/core/src/transformers/MethodTransformer.ts +++ b/packages/core/src/transformers/MethodTransformer.ts @@ -78,20 +78,22 @@ const get_escaped_type = const escape_promise = (checker: ts.TypeChecker) => (type: ts.Type): ts.Type => { - const generic: readonly ts.Type[] = checker.getTypeArguments( - type as ts.TypeReference, - ); - if (generic.length !== 1) - throw new Error( - "Error on ImportAnalyzer.analyze(): invalid promise type.", + try { + const generic: readonly ts.Type[] = checker.getTypeArguments( + type as ts.TypeReference, ); - return generic[0]!; + if (!generic?.length) return type; + return generic[0] ?? type; + } catch { + return type; + } }; -const get_name = (symbol: ts.Symbol): string => - explore_name(symbol.getDeclarations()![0]!.parent)( - symbol.escapedName.toString(), - ); +const get_name = (symbol: ts.Symbol): string => { + const parent: ts.Node | undefined = symbol.getDeclarations()?.[0]?.parent; + const name: string = symbol.escapedName?.toString() ?? ""; + return parent ? explore_name(parent)(name) : name; +}; const explore_name = (decl: ts.Node) => diff --git a/tests/test-sdk/.gitignore b/tests/test-sdk/.gitignore index a562634892..bd7b71cec5 100644 --- a/tests/test-sdk/.gitignore +++ b/tests/test-sdk/.gitignore @@ -6,6 +6,7 @@ features/*/src/test/features/api/automated features/clone*/src/api/structures features/clone-type-create-duplicate/sdk +features/method-transformer-defensive/src/api/structures features/configurations/src/api/bbs features/configurations/src/api/common features/configurations/*.swagger.json \ No newline at end of file diff --git a/tests/test-sdk/features/method-transformer-defensive/nestia.config.ts b/tests/test-sdk/features/method-transformer-defensive/nestia.config.ts new file mode 100644 index 0000000000..f75934d1f7 --- /dev/null +++ b/tests/test-sdk/features/method-transformer-defensive/nestia.config.ts @@ -0,0 +1,11 @@ +import { INestiaConfig } from "@nestia/sdk"; + +export const NESTIA_CONFIG: INestiaConfig = { + input: ["src/controllers"], + output: "src/api", + clone: true, + swagger: { + output: "swagger.json", + }, +}; +export default NESTIA_CONFIG; diff --git a/tests/test-sdk/features/method-transformer-defensive/src/controllers/ImportTypeController.ts b/tests/test-sdk/features/method-transformer-defensive/src/controllers/ImportTypeController.ts new file mode 100644 index 0000000000..3ebf7739f1 --- /dev/null +++ b/tests/test-sdk/features/method-transformer-defensive/src/controllers/ImportTypeController.ts @@ -0,0 +1,35 @@ +import { TypedRoute } from "@nestia/core"; +import { Controller } from "@nestjs/common"; + +@Controller("import-type") +export class ImportTypeController { + @TypedRoute.Get("envelope") + public envelope(): IEnvelope { + return { message: "ok", data: { greeting: "hello" } }; + } + + @TypedRoute.Get("alias") + public alias(): AliasEnvelope { + return { message: "ok", data: { greeting: "hello" } }; + } + + @TypedRoute.Get< + import("./ImportTypeController").IEnvelope< + import("./ImportTypeController").IGreeting + > + >("decorator-generic") + public decoratorGeneric(): AliasEnvelope { + return { message: "ok", data: { greeting: "hello" } }; + } +} + +export interface IEnvelope { + message: string; + data: T | null; +} + +export interface IGreeting { + greeting: string; +} + +type AliasEnvelope = IEnvelope; diff --git a/tests/test-sdk/features/method-transformer-defensive/src/controllers/InlineReturnController.ts b/tests/test-sdk/features/method-transformer-defensive/src/controllers/InlineReturnController.ts new file mode 100644 index 0000000000..94256a1159 --- /dev/null +++ b/tests/test-sdk/features/method-transformer-defensive/src/controllers/InlineReturnController.ts @@ -0,0 +1,42 @@ +import { TypedRoute } from "@nestia/core"; +import { Controller } from "@nestjs/common"; + +@Controller("inline-return") +export class InlineReturnController { + @TypedRoute.Get("anonymous") + public anonymous(): Promise<{ foo: string; bar?: number }> { + return Promise.resolve({ foo: "ok" }); + } + + @TypedRoute.Get("intersection") + public intersection(): Promise { + return Promise.resolve({ x: 1, y: "hi" }); + } + + @TypedRoute.Get("return-type") + public returnType(): Promise> { + return Promise.resolve(getItem()); + } + + @TypedRoute.Get("inferred") + public inferred(): Promise IItem>> { + return Promise.resolve({ id: 1, label: "item" }); + } +} + +interface IWithX { + x: number; +} +interface IWithY { + y: string; +} +interface IItem { + id: number; + label: string; +} + +type InferReturn = T extends (...args: any[]) => infer R ? R : never; + +function getItem(): IItem { + return { id: 1, label: "item" }; +} diff --git a/tests/test-sdk/features/method-transformer-defensive/tsconfig.json b/tests/test-sdk/features/method-transformer-defensive/tsconfig.json new file mode 100644 index 0000000000..ccd8aec38c --- /dev/null +++ b/tests/test-sdk/features/method-transformer-defensive/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../config/tsconfig.json", + "compilerOptions": { + "paths": { + "@api": ["./src/api"], + "@api/lib/*": ["./src/api/*"], + }, + }, + "include": ["src"], +}