From cdcef7df9600d15203c7f4ccb170fc659db4c573 Mon Sep 17 00:00:00 2001 From: Sven Liebig <9104499+svenliebig@users.noreply.github.com> Date: Mon, 4 Sep 2023 10:41:56 +0200 Subject: [PATCH] feat: add undefined type and identifier to some types --- src/models/ArrayType.ts | 7 ++++--- src/models/IntersectionType.ts | 4 ++-- src/models/IntersectionTypeDeclaration.ts | 17 ++++++++--------- src/models/StringType.ts | 6 ++++++ src/models/StringTypeDeclaration.ts | 13 ++++++------- src/models/TypeLiteral.ts | 2 +- src/models/UndefinedType.ts | 7 +++++++ src/models/UnionType.ts | 4 ++-- src/models/UnionTypeDeclaration.ts | 17 ++++++++--------- src/rewrite.ts | 11 +++++++++-- src/utils/DeclarationFactory.ts | 2 +- src/utils/TypeFactory.ts | 18 +++++++++++++----- src/utils/isUndefinedKeywordTypeNode.ts | 5 +++++ tests/fixtures/typeLiteral.ts | 14 +++++++++++++- tests/typeLiteralDeclaration.test.ts | 18 ++++++++++++++++++ 15 files changed, 103 insertions(+), 42 deletions(-) create mode 100644 src/models/UndefinedType.ts create mode 100644 src/utils/isUndefinedKeywordTypeNode.ts diff --git a/src/models/ArrayType.ts b/src/models/ArrayType.ts index 7e89b74..cb4070f 100644 --- a/src/models/ArrayType.ts +++ b/src/models/ArrayType.ts @@ -19,16 +19,17 @@ export class ArrayType extends Type { super() // TODO this should not be necessary - this.arrayType = new StringType() + // TODO add declarion to this later + this.arrayType = new StringType(undefined as any) if (isTypeReferenceNode(node)) { if (node.typeArguments) { for (const argument of node.typeArguments) { - this.arrayType = TypeFactory.create(argument) + this.arrayType = TypeFactory.create(argument, "") // TODO } } } else { - this.arrayType = TypeFactory.create(node.elementType) + this.arrayType = TypeFactory.create(node.elementType, "") // TODO } } diff --git a/src/models/IntersectionType.ts b/src/models/IntersectionType.ts index e36fcf9..1759252 100644 --- a/src/models/IntersectionType.ts +++ b/src/models/IntersectionType.ts @@ -5,9 +5,9 @@ import { Type } from "./Type" export class IntersectionType extends Type { public types: Array - constructor(node: IntersectionTypeNode) { + constructor(node: IntersectionTypeNode, public identifier: string) { super() - this.types = node.types.map(TypeFactory.create) + this.types = node.types.map((t) => TypeFactory.create(t, identifier)) } toString(): string { diff --git a/src/models/IntersectionTypeDeclaration.ts b/src/models/IntersectionTypeDeclaration.ts index 7d62e9a..1256840 100644 --- a/src/models/IntersectionTypeDeclaration.ts +++ b/src/models/IntersectionTypeDeclaration.ts @@ -1,18 +1,17 @@ -import { IntersectionTypeNode } from "typescript"; -import { IntersectionType } from "./IntersectionType"; -import { DeclarationMeta } from "../parser"; -import { TypeDeclaration } from "./TypeDeclaration"; - +import { IntersectionTypeNode } from "typescript" +import { IntersectionType } from "./IntersectionType" +import { DeclarationMeta } from "../parser" +import { TypeDeclaration } from "./TypeDeclaration" export class IntersectionTypeDeclaration extends TypeDeclaration { - public type: IntersectionType; + public type: IntersectionType constructor(meta: DeclarationMeta, node: IntersectionTypeNode) { - super(meta); - this.type = new IntersectionType(node); + super(meta) + this.type = new IntersectionType(node, this.identifier) } typeToString(): string { - return this.type.toString(); + return this.type.toString() } } diff --git a/src/models/StringType.ts b/src/models/StringType.ts index 612a3c0..6720291 100644 --- a/src/models/StringType.ts +++ b/src/models/StringType.ts @@ -1,6 +1,12 @@ +import { StringTypeDeclaration } from "./StringTypeDeclaration" import { Type } from "./Type" +import { TypeDeclaration } from "./TypeDeclaration" export class StringType extends Type { + constructor(public identifier: string) { + super() + } + toString() { return "string" } diff --git a/src/models/StringTypeDeclaration.ts b/src/models/StringTypeDeclaration.ts index 4e86db2..1ec01c4 100644 --- a/src/models/StringTypeDeclaration.ts +++ b/src/models/StringTypeDeclaration.ts @@ -1,16 +1,15 @@ -import { DeclarationMeta } from "../parser"; -import { TypeDeclaration } from "./TypeDeclaration"; -import { StringType } from "./StringType"; - +import { DeclarationMeta } from "../parser" +import { TypeDeclaration } from "./TypeDeclaration" +import { StringType } from "./StringType" export class StringTypeDeclaration extends TypeDeclaration { - public type: StringType = new StringType(); + public type: StringType = new StringType(this.identifier) constructor(meta: DeclarationMeta) { - super(meta); + super(meta) } typeToString(): string { - return "string"; + return "string" } } diff --git a/src/models/TypeLiteral.ts b/src/models/TypeLiteral.ts index f4fea15..3fe83e9 100644 --- a/src/models/TypeLiteral.ts +++ b/src/models/TypeLiteral.ts @@ -12,7 +12,7 @@ export class TypeLiteral extends Type { if (isPropertySignature(member)) { if (isIdentifier(member.name)) { if (member.type) { - this.properties.set(member.name.text, TypeFactory.create(member.type)) + this.properties.set(member.name.text, TypeFactory.create(member.type, member.name.text)) } else { throw new Error(`Member has no type node: ${member.name.text}`) } diff --git a/src/models/UndefinedType.ts b/src/models/UndefinedType.ts new file mode 100644 index 0000000..2f0d689 --- /dev/null +++ b/src/models/UndefinedType.ts @@ -0,0 +1,7 @@ +import { Type } from "./Type" + +export class UndefinedType extends Type { + toString() { + return "undefined" + } +} diff --git a/src/models/UnionType.ts b/src/models/UnionType.ts index dd442cf..c2e235a 100644 --- a/src/models/UnionType.ts +++ b/src/models/UnionType.ts @@ -6,9 +6,9 @@ export class UnionType extends Type { public types: Array // TODO these `types` should be a parameter instead of creating a dependency to the `typescript` lib here - constructor(node: UnionTypeNode) { + constructor(node: UnionTypeNode, public identifier: string) { super() - this.types = node.types.map(TypeFactory.create) + this.types = node.types.map((t) => TypeFactory.create(t, this.identifier)) } toString(): string { diff --git a/src/models/UnionTypeDeclaration.ts b/src/models/UnionTypeDeclaration.ts index 5f1a641..7829068 100644 --- a/src/models/UnionTypeDeclaration.ts +++ b/src/models/UnionTypeDeclaration.ts @@ -1,18 +1,17 @@ -import { UnionTypeNode } from "typescript"; -import { UnionType } from "./UnionType"; -import { DeclarationMeta } from "../parser"; -import { TypeDeclaration } from "./TypeDeclaration"; - +import { UnionTypeNode } from "typescript" +import { UnionType } from "./UnionType" +import { DeclarationMeta } from "../parser" +import { TypeDeclaration } from "./TypeDeclaration" export class UnionTypeDeclaration extends TypeDeclaration { - public type: UnionType; + public type: UnionType constructor(meta: DeclarationMeta, node: UnionTypeNode) { - super(meta); - this.type = new UnionType(node); + super(meta) + this.type = new UnionType(node, this.identifier) } typeToString(): string { - return this.type.toString(); + return this.type.toString() } } diff --git a/src/rewrite.ts b/src/rewrite.ts index 18b45e2..813ed4d 100644 --- a/src/rewrite.ts +++ b/src/rewrite.ts @@ -6,6 +6,7 @@ import { StringType } from "./models/StringType" import { Type } from "./models/Type" import { TypeLiteral } from "./models/TypeLiteral" import { TypeReference } from "./models/TypeReference" +import { UndefinedType } from "./models/UndefinedType" import { UnionType } from "./models/UnionType" import { UnknownType } from "./models/UnknownType" @@ -20,6 +21,7 @@ export type TypeTransformers = { string?(type: StringType): string number?(type: NumberType): string reference?(type: TypeReference): string + undefined?(type: UndefinedType): string } export function rewrite(type: Type, transformers: TypeTransformers): string { @@ -47,6 +49,10 @@ export function rewrite(type: Type, transformers: TypeTransformers): string { return transformers.reference?.(type) ?? type.toString() } + if (type instanceof UndefinedType) { + return transformers.undefined?.(type) ?? type.toString() + } + if (type instanceof IntersectionType) { return type.types .filter(filterUnknown) @@ -62,7 +68,8 @@ export function rewrite(type: Type, transformers: TypeTransformers): string { if (isOnlyBasicTypes(type.types)) { return rewrite(type.types[0], transformers) } - return "Union..." + + return rewrite(type.types[0], transformers) // + " | " + rewrite(type.types[1], transformers) } if (type instanceof TypeLiteral) { @@ -79,5 +86,5 @@ export function rewrite(type: Type, transformers: TypeTransformers): string { } function isOnlyBasicTypes(types: Array) { - return types.every((type) => [BooleanType, StringType, UnknownType, NumberType, LiteralType].some((basic) => type instanceof basic)) + return types.every((type) => [BooleanType, StringType, UnknownType, NumberType, LiteralType, UndefinedType].some((basic) => type instanceof basic)) } diff --git a/src/utils/DeclarationFactory.ts b/src/utils/DeclarationFactory.ts index 7ae1aae..d51022e 100644 --- a/src/utils/DeclarationFactory.ts +++ b/src/utils/DeclarationFactory.ts @@ -97,7 +97,7 @@ export class DeclarationFactory { return new TypeLiteralDeclaration(meta, statement.type) } - throw new Error(`Unknown TypeNode kind: ${statement.type.kind}`) + throw new Error(`DeclarationFactory: Unknown TypeNode kind '${statement.type.kind}'`) } public static createMeta(statement: TypeAliasDeclaration | EnumDeclaration): DeclarationMeta { diff --git a/src/utils/TypeFactory.ts b/src/utils/TypeFactory.ts index 9048931..bfcab02 100644 --- a/src/utils/TypeFactory.ts +++ b/src/utils/TypeFactory.ts @@ -14,6 +14,7 @@ import { IntersectionType } from "../models/IntersectionType" import { LiteralType } from "../models/LiteralType" import { NumberType } from "../models/NumberType" import { StringType } from "../models/StringType" +import { TypeDeclaration } from "../models/TypeDeclaration" import { TypeLiteral } from "../models/TypeLiteral" import { TypeReference } from "../models/TypeReference" import { UnionType } from "../models/UnionType" @@ -21,9 +22,11 @@ import { isBooleanKeywordTypeNode } from "./isBooleanKeywordTypeNode" import { isNumberKeywordTypeNode } from "./isNumberKeywordTypeNode" import { isStringKeywordTypeNode } from "./isStringKeywordTypeNode" import { L } from "./logger" +import { isUndefinedKeywordTypeNode } from "./isUndefinedKeywordTypeNode" +import { UndefinedType } from "../models/UndefinedType" export class TypeFactory { - static create(node: TypeNode) { + static create(node: TypeNode, identifier: string) { L.d(``, node.kind) if (isArrayTypeNode(node)) { @@ -35,15 +38,16 @@ export class TypeFactory { } if (isUnionTypeNode(node)) { - return new UnionType(node) + // TODO identifier is not resolved yet + return new UnionType(node, identifier) } if (isIntersectionTypeNode(node)) { - return new IntersectionType(node) + return new IntersectionType(node, identifier) } if (isStringKeywordTypeNode(node)) { - return new StringType() + return new StringType(identifier) } if (isNumberKeywordTypeNode(node)) { @@ -66,6 +70,10 @@ export class TypeFactory { return new BooleanType() } - throw new Error(`Unknown TypeNode kind: ${node.kind}`) + if (isUndefinedKeywordTypeNode(node)) { + return new UndefinedType() + } + + throw new Error(`TypeFactory: Unknown TypeNode kind '${node.kind}'`) } } diff --git a/src/utils/isUndefinedKeywordTypeNode.ts b/src/utils/isUndefinedKeywordTypeNode.ts new file mode 100644 index 0000000..e0852f4 --- /dev/null +++ b/src/utils/isUndefinedKeywordTypeNode.ts @@ -0,0 +1,5 @@ +import { KeywordTypeNode, SyntaxKind, TypeNode } from "typescript" + +export function isUndefinedKeywordTypeNode(node: TypeNode): node is KeywordTypeNode { + return node.kind === SyntaxKind.UndefinedKeyword +} diff --git a/tests/fixtures/typeLiteral.ts b/tests/fixtures/typeLiteral.ts index d21c04f..e4ceb37 100644 --- a/tests/fixtures/typeLiteral.ts +++ b/tests/fixtures/typeLiteral.ts @@ -41,4 +41,16 @@ export type ExportedComplexNestedTypeLiteralWithImports = { nested: { object: BasicTypeLiteral } -} \ No newline at end of file +} + +type WithUndefined = { + undefined: undefined +} + +type WithUnionUndefinedAndString = { + union: undefined | string +} + +type WithNull = { + null: null +} diff --git a/tests/typeLiteralDeclaration.test.ts b/tests/typeLiteralDeclaration.test.ts index f2dcfb1..8c7c078 100644 --- a/tests/typeLiteralDeclaration.test.ts +++ b/tests/typeLiteralDeclaration.test.ts @@ -60,3 +60,21 @@ test("toString for ComplexNestedTypeLiteralWithImports after resolve", (t) => { const type = "{ union: string | number, nested: { object: { str: string, num: number } } }" t.is(str, `type ComplexNestedTypeLiteralWithImports = ${type}`) }) + +test("toString for WithNull after resolve", (t) => { + const str = parser.resolve("WithNull")?.toString() + const type = "{ null: null }" + t.is(str, `type WithNull = ${type}`) +}) + +test("toString for WithUndefined after resolve", (t) => { + const str = parser.resolve("WithUndefined")?.toString() + const type = "{ undefined: undefined }" + t.is(str, `type WithUndefined = ${type}`) +}) + +test("toString for WithUnionUndefinedAndString after resolve", (t) => { + const str = parser.resolve("WithUnionUndefinedAndString")?.toString() + const type = "{ union: undefined | string }" + t.is(str, `type WithUnionUndefinedAndString = ${type}`) +})