diff --git a/plugins/typescript/src/core/schemaToEnumDeclaration.ts b/plugins/typescript/src/core/schemaToEnumDeclaration.ts index 3ed256f..c88fd0e 100644 --- a/plugins/typescript/src/core/schemaToEnumDeclaration.ts +++ b/plugins/typescript/src/core/schemaToEnumDeclaration.ts @@ -1,33 +1,10 @@ import { pascal } from "case"; import { SchemaObject } from "openapi3-ts/oas30"; import ts, { factory as f } from "typescript"; +import { isValidIdentifier } from "tsutils"; import { convertNumberToWord } from "../utils/getEnumProperties"; import { Context, getJSDocComment } from "./schemaToTypeAliasDeclaration"; -/** - * Function to check if a string is a valid TypeScript identifier - * - * @param name Name to check - */ -function isValidIdentifier(name: string): boolean { - if (name.length === 0) { - return false; - } - - const firstChar = name.charCodeAt(0); - if (!ts.isIdentifierStart(firstChar, ts.ScriptTarget.Latest)) { - return false; - } - - for (let i = 1; i < name.length; i++) { - if (!ts.isIdentifierPart(name.charCodeAt(i), ts.ScriptTarget.Latest)) { - return false; - } - } - - return true; -} - /** * Add Enum support when transforming an OpenAPI Schema Object to Typescript Nodes. * diff --git a/plugins/typescript/src/core/schemaToTypeAliasDeclaration.test.ts b/plugins/typescript/src/core/schemaToTypeAliasDeclaration.test.ts index e884896..de2f20b 100644 --- a/plugins/typescript/src/core/schemaToTypeAliasDeclaration.test.ts +++ b/plugins/typescript/src/core/schemaToTypeAliasDeclaration.test.ts @@ -946,6 +946,30 @@ describe("schemaToTypeAliasDeclaration", () => { `); }); + it("should generate valid identifier from number name", () => { + const schema: SchemaObject = {}; + + expect(printSchema(schema, "200")).toMatchInlineSnapshot(` + "export type _200 = void;" + `); + }); + + it("should generate valid identifier from symbol name", () => { + const schema: SchemaObject = {}; + + expect(printSchema(schema, "-")).toMatchInlineSnapshot(` + "export type _ = void;" + `); + }); + + it("should generate valid identifier from invalid name", () => { + const schema: SchemaObject = {}; + + expect(printSchema(schema, "🙂")).toMatchInlineSnapshot(` + "export type _ = void;" + `); + }); + it("should generate a `never` if the combined type is broken", () => { const schema: SchemaObject = { allOf: [{ type: "string" }, { type: "number" }], diff --git a/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts b/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts index 1d9b4a1..e2f09d1 100644 --- a/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts +++ b/plugins/typescript/src/core/schemaToTypeAliasDeclaration.ts @@ -11,7 +11,7 @@ import { } from "openapi3-ts/oas30"; import { singular } from "pluralize"; import { isValidIdentifier } from "tsutils"; -import ts, { factory as f } from "typescript"; +import ts, { factory as f, isIdentifierStart } from "typescript"; import { getReferenceSchema } from "./getReference"; type RemoveIndex = { @@ -56,11 +56,24 @@ export const schemaToTypeAliasDeclaration = ( const jsDocNode = isSchemaObject(schema) ? getJSDocComment(schema, context) : undefined; + + let identifier = pascal(name); + + // If the identifier does not start with a valid character, prefix it with an underscore. + if (!isIdentifierStart(identifier.charCodeAt(0), ts.ScriptTarget.Latest)) { + identifier = `_${identifier}`; + } + + // If the identifier is still not valid, remove invalid characters. + if (!isValidIdentifier(identifier)) { + identifier = identifier.replace(/[^a-zA-Z0-9_]/g, ""); + } + const declarationNode = f.createTypeAliasDeclaration( [f.createModifier(ts.SyntaxKind.ExportKeyword)], - pascal(name), + identifier, undefined, - getType(schema, context, name) + getType(schema, context, identifier) ); return jsDocNode ? [jsDocNode, declarationNode] : [declarationNode]; @@ -89,10 +102,10 @@ export const getType = ( let refNode: ts.TypeNode = f.createTypeReferenceNode( namespace === context.currentComponent - ? f.createIdentifier(pascal(name)) + ? f.createIdentifier(name) : f.createQualifiedName( f.createIdentifier(pascal(namespace)), - f.createIdentifier(pascal(name)) + f.createIdentifier(name) ) ); @@ -173,7 +186,7 @@ export const getType = ( if (schema.enum) { if (isNodeEnum) { - return f.createTypeReferenceNode(f.createIdentifier(pascal(name || ""))); + return f.createTypeReferenceNode(f.createIdentifier(name || "")); } const unionTypes = f.createUnionTypeNode([