Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Go back to generating enums out of unions containing enums #2149

Merged
merged 14 commits into from
Jan 7, 2025
Merged
31 changes: 24 additions & 7 deletions src/TypeFormatter/LiteralUnionTypeFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Definition } from "../Schema/Definition.js";
import { RawTypeName } from "../Schema/RawType.js";
import { SubTypeFormatter } from "../SubTypeFormatter.js";
import { BaseType } from "../Type/BaseType.js";
import { EnumType } from "../Type/EnumType.js";
import { LiteralType, LiteralValue } from "../Type/LiteralType.js";
import { NullType } from "../Type/NullType.js";
import { StringType } from "../Type/StringType.js";
Expand Down Expand Up @@ -43,8 +44,8 @@ export class LiteralUnionTypeFormatter implements SubTypeFormatter {
};
}

const values = uniqueArray(types.map(getLiteralValue));
const typeNames = uniqueArray(types.map(getLiteralType));
const values = uniqueArray(types.flatMap(getLiteralValues));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can probably become map again if we fix the thing below

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it for us since i also need this PR :)

351e144

const typeNames = uniqueArray(types.flatMap(getLiteralTypes));

const ret = {
type: typeNames.length === 1 ? typeNames[0] : typeNames,
Expand Down Expand Up @@ -72,13 +73,29 @@ export class LiteralUnionTypeFormatter implements SubTypeFormatter {
export function isLiteralUnion(type: UnionType): boolean {
return type
.getFlattenedTypes()
.every((item) => item instanceof LiteralType || item instanceof NullType || item instanceof StringType);
.every(
(item) =>
item instanceof LiteralType ||
item instanceof NullType ||
item instanceof StringType ||
item instanceof EnumType,
);
}

function getLiteralValue(value: LiteralType | NullType): LiteralValue | null {
return value instanceof LiteralType ? value.getValue() : null;
function getLiteralValues(value: LiteralType | EnumType | NullType): readonly (LiteralValue | null)[] {
if (value instanceof EnumType) {
return value.getValues();
} else if (value instanceof LiteralType) {
arthurfiorette marked this conversation as resolved.
Show resolved Hide resolved
return [value.getValue()];
}
return [null];
arthurfiorette marked this conversation as resolved.
Show resolved Hide resolved
}

function getLiteralType(value: LiteralType | NullType): RawTypeName {
return value instanceof LiteralType ? typeName(value.getValue()) : "null";
function getLiteralTypes(value: LiteralType | EnumType | NullType): RawTypeName[] {
if (value instanceof EnumType) {
return value.getValues().map(typeName);
} else if (value instanceof LiteralType) {
arthurfiorette marked this conversation as resolved.
Show resolved Hide resolved
return [typeName(value.getValue())];
}
return ["null"];
}
1 change: 1 addition & 0 deletions test/valid-data-other.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe("valid-data-other", () => {
it("enums-mixed", assertValidSchema("enums-mixed", "Enum"));
it("enums-member", assertValidSchema("enums-member", "MyObject"));
it("enums-template-literal", assertValidSchema("enums-template-literal", "MyObject"));
it("enums-union", assertValidSchema("enums-union", "MyObject"));

it("function-parameters-default-value", assertValidSchema("function-parameters-default-value", "myFunction"));
it("function-parameters-declaration", assertValidSchema("function-parameters-declaration", "myFunction"));
Expand Down
26 changes: 26 additions & 0 deletions test/valid-data/enums-union/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
enum Alphabet {
Alpha = "alpha",
Beta = "beta",
Omega = 666,
}

enum FileAccess {
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
}

export type MyObject = {
// All the members above should be output as enums, not anyOf
enumMembers: Alphabet.Alpha | Alphabet.Beta;
enumMemberWithLiteral: Alphabet.Alpha | "foo";
enumMemberWithLiteralAndNull: Alphabet.Alpha | "foo" | null;
enumMemberWithInterface: Alphabet.Alpha | { abc: string };
enumMembersWithNumber: Alphabet.Alpha | Alphabet.Omega;
wholeEnum: Alphabet; // Should output just all of Alphabet
wholeEnumWithLiteral: Alphabet | "bar"; // Should output all of Alphabet members (2 strings, 1 number) and "bar"
wholeEnumWithLiteralAndNull: Alphabet | "bar" | null;
arthurfiorette marked this conversation as resolved.
Show resolved Hide resolved
twoDifferentEnumMembers: Alphabet.Alpha | FileAccess.Read;
twoDifferentWholeEnums: Alphabet | FileAccess;
};
141 changes: 141 additions & 0 deletions test/valid-data/enums-union/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"$ref": "#/definitions/MyObject",
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject": {
"additionalProperties": false,
"properties": {
"enumMemberWithInterface": {
"anyOf": [
{
"const": "alpha",
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"abc": {
"type": "string"
}
},
"required": [
"abc"
],
"type": "object"
}
]
},
"enumMemberWithLiteral": {
"enum": [
"alpha",
"foo"
],
"type": "string"
},
"enumMemberWithLiteralAndNull": {
"enum": [
"alpha",
"foo",
null
],
"type": [
"string",
"null"
]
},
"enumMembers": {
"enum": [
"alpha",
"beta"
],
"type": "string"
},
"enumMembersWithNumber": {
"enum": [
"alpha",
666
],
"type": [
"string",
"number"
]
},
"twoDifferentEnumMembers": {
"enum": [
"alpha",
2
],
"type": [
"string",
"number"
]
},
"twoDifferentWholeEnums": {
"enum": [
arthurfiorette marked this conversation as resolved.
Show resolved Hide resolved
"alpha",
"beta",
666,
0,
2,
4,
6
],
"type": [
"string",
"number"
]
},
"wholeEnum": {
"enum": [
"alpha",
"beta",
666
],
"type": [
"string",
"number"
]
},
"wholeEnumWithLiteral": {
"enum": [
"alpha",
"beta",
666,
"bar"
],
"type": [
"string",
"number"
]
},
"wholeEnumWithLiteralAndNull": {
"enum": [
"alpha",
"beta",
666,
"bar",
null
],
"type": [
"string",
"number",
"null"
]
}
},
"required": [
"enumMembers",
"enumMemberWithLiteral",
"enumMemberWithLiteralAndNull",
"enumMemberWithInterface",
"enumMembersWithNumber",
"wholeEnum",
"wholeEnumWithLiteral",
"wholeEnumWithLiteralAndNull",
"twoDifferentEnumMembers",
"twoDifferentWholeEnums"
],
"type": "object"
}
}
}
Loading