diff --git a/src/NodeParser/IntersectionNodeParser.ts b/src/NodeParser/IntersectionNodeParser.ts index da200a971..d94416a34 100644 --- a/src/NodeParser/IntersectionNodeParser.ts +++ b/src/NodeParser/IntersectionNodeParser.ts @@ -11,6 +11,8 @@ import { UndefinedType } from "../Type/UndefinedType.js"; import { NeverType } from "../Type/NeverType.js"; import { ObjectType } from "../Type/ObjectType.js"; import { StringType } from "../Type/StringType.js"; +import { LiteralType } from "../Type/LiteralType.js"; +import { isLiteralUnion } from "../TypeFormatter/LiteralUnionTypeFormatter.js"; export class IntersectionNodeParser implements SubNodeParser { public constructor( @@ -31,8 +33,14 @@ export class IntersectionNodeParser implements SubNodeParser { } // handle autocomplete hacks like `string & {}` - if (types.length === 2 && types.some((t) => t instanceof StringType) && types.some((t) => isEmptyObject(t))) { - return new StringType(true); + if (types.length === 2 && types.some((t) => isEmptyObject(t))) { + if (types.some((t) => t instanceof StringType)) { + return new StringType(true); + } + const nonObject = types.find((t) => !isEmptyObject(t)); + if (nonObject instanceof LiteralType || (nonObject instanceof UnionType && isLiteralUnion(nonObject))) { + return nonObject; + } } return translate(types); diff --git a/src/TypeFormatter/LiteralUnionTypeFormatter.ts b/src/TypeFormatter/LiteralUnionTypeFormatter.ts index 51935c98a..68572bfce 100644 --- a/src/TypeFormatter/LiteralUnionTypeFormatter.ts +++ b/src/TypeFormatter/LiteralUnionTypeFormatter.ts @@ -83,7 +83,7 @@ function flattenTypes(type: UnionType): (StringType | LiteralType | NullType)[] }); } -function isLiteralUnion(type: UnionType): boolean { +export function isLiteralUnion(type: UnionType): boolean { return flattenTypes(type).every( (item) => item instanceof LiteralType || item instanceof NullType || item instanceof StringType, ); diff --git a/test/valid-data/string-literals-hack/main.ts b/test/valid-data/string-literals-hack/main.ts index 57489b475..81950cb8b 100644 --- a/test/valid-data/string-literals-hack/main.ts +++ b/test/valid-data/string-literals-hack/main.ts @@ -11,4 +11,5 @@ export type MyObject = { withHack: "foo" | "bar" | (string & {}); withHackRecord: "foo" | "bar" | (string & Record); withHackNull: "foo" | "bar" | null | (string & Record); + hackOnLiteral: ("foo" & Record) | "bar"; }; diff --git a/test/valid-data/string-literals-hack/schema.json b/test/valid-data/string-literals-hack/schema.json index 388eceac0..e2640ba69 100644 --- a/test/valid-data/string-literals-hack/schema.json +++ b/test/valid-data/string-literals-hack/schema.json @@ -5,6 +5,13 @@ "MyObject": { "additionalProperties": false, "properties": { + "hackOnLiteral": { + "enum": [ + "foo", + "bar" + ], + "type": "string" + }, "literalWithNull": { "enum": [ "foo", @@ -106,7 +113,8 @@ "withRefWithString", "withHack", "withHackRecord", - "withHackNull" + "withHackNull", + "hackOnLiteral" ], "type": "object" }