Skip to content

Commit 5b0cbf9

Browse files
committed
Export type-predicates along when enumValues is true
1 parent 9b23607 commit 5b0cbf9

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

packages/openapi-typescript/src/transform/schema-object.ts

+130-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,130 @@ function transformEnumSchemaObjectToUnion(schemaObject: EnumSchemaObject) {
8484
return tsUnion(isNullable ? enumType.concat(NULL) : enumType);
8585
}
8686

87+
const createTypePredicateHelper = ts.factory.createFunctionDeclaration(
88+
/* modifiers */ undefined,
89+
/* asteriskToken */ undefined,
90+
/* name */ ts.factory.createIdentifier("get_is"),
91+
/* typeParameters */ [
92+
ts.factory.createTypeParameterDeclaration(
93+
/* modifiers */ undefined,
94+
/* name */ ts.factory.createIdentifier("TItem"),
95+
/* constraint */ tsUnion([NUMBER, STRING]),
96+
/* defaultType */ undefined,
97+
),
98+
],
99+
/* parameters */ [
100+
ts.factory.createParameterDeclaration(
101+
/* modifiers */ undefined,
102+
/* dotDotDotToken */ undefined,
103+
/* name */ ts.factory.createIdentifier("items"),
104+
/* questionToken */ undefined,
105+
/* type */ ts.factory.createTypeOperatorNode(
106+
/* operator */ ts.SyntaxKind.ReadonlyKeyword,
107+
/* type */ ts.factory.createArrayTypeNode(
108+
/* elementType */ ts.factory.createTypeReferenceNode(
109+
/* typeName */ ts.factory.createIdentifier("TItem"),
110+
/* typeArguments */ undefined,
111+
),
112+
),
113+
),
114+
/* initializer */ undefined,
115+
),
116+
],
117+
/* type */ undefined,
118+
/* body */ ts.factory.createBlock(
119+
/* statements */ [
120+
ts.factory.createReturnStatement(
121+
ts.factory.createArrowFunction(
122+
/* modifiers */ undefined,
123+
/* typeParameters */ undefined,
124+
/* parameters */ [
125+
ts.factory.createParameterDeclaration(
126+
/* modifiers */ undefined,
127+
/* dotDotDotToken */ undefined,
128+
/* name */ ts.factory.createIdentifier("value"),
129+
/* questionToken */ undefined,
130+
/* type */ tsUnion([NULL, NUMBER, STRING, UNDEFINED]),
131+
/* initializer */ undefined,
132+
),
133+
],
134+
/* type */ ts.factory.createTypePredicateNode(
135+
/* assertsModifier */ undefined,
136+
/* parameterName */ ts.factory.createIdentifier("value"),
137+
/* type */ ts.factory.createTypeReferenceNode("TItem"),
138+
),
139+
/* equalsGreaterThanToken */ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
140+
/* body */ ts.factory.createBlock(
141+
/* statements */ [
142+
ts.factory.createReturnStatement(
143+
/* expression */ ts.factory.createBinaryExpression(
144+
/* left */ ts.factory.createBinaryExpression(
145+
/* left */ ts.factory.createBinaryExpression(
146+
/* left */ ts.factory.createIdentifier("value"),
147+
/* operator*/ ts.SyntaxKind.ExclamationEqualsEqualsToken,
148+
/* right */ ts.factory.createIdentifier("null"),
149+
),
150+
/* operator */ ts.SyntaxKind.AmpersandAmpersandToken,
151+
/* right */ ts.factory.createBinaryExpression(
152+
/* left */ ts.factory.createIdentifier("value"),
153+
/* operator */ ts.SyntaxKind.ExclamationEqualsEqualsToken,
154+
/* right */ ts.factory.createIdentifier("undefined"),
155+
),
156+
),
157+
/* operator */ ts.SyntaxKind.AmpersandAmpersandToken,
158+
/* right */ ts.factory.createCallExpression(
159+
/* expression */ ts.factory.createPropertyAccessExpression(
160+
/* expression */ ts.factory.createIdentifier("items"),
161+
/* name */ ts.factory.createIdentifier("includes"),
162+
),
163+
/* typeArguments */ undefined,
164+
/* argumentsArray */ [
165+
ts.factory.createAsExpression(
166+
/* expression */ ts.factory.createIdentifier("value"),
167+
/* type */ ts.factory.createTypeReferenceNode(
168+
/* typeName */ ts.factory.createIdentifier("TItem"),
169+
/* typeArguments */ undefined,
170+
),
171+
),
172+
],
173+
),
174+
),
175+
),
176+
],
177+
/* multiLine */ true,
178+
),
179+
),
180+
),
181+
],
182+
/* multiline */ true,
183+
),
184+
);
185+
186+
function getTypePredicate(constName: string, constTypeName: string, options: TransformNodeOptions) {
187+
if (!options.ctx.injectNodes.includes(createTypePredicateHelper)) {
188+
options.ctx.injectNodes.push(createTypePredicateHelper);
189+
}
190+
191+
return ts.factory.createVariableStatement(
192+
/* modifiers */ tsModifiers({ export: true }),
193+
/* declarationList */ ts.factory.createVariableDeclarationList(
194+
/* declarations */ [
195+
ts.factory.createVariableDeclaration(
196+
/* name */ ts.factory.createIdentifier(`is_${constTypeName}`),
197+
/* exclamationToken */ undefined,
198+
/* type */ undefined,
199+
/* initializer */ ts.factory.createCallExpression(
200+
/* expression */ ts.factory.createIdentifier("get_is"),
201+
/* typeArguments */ [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(constTypeName))],
202+
/* argumentsArray */ [ts.factory.createIdentifier(constName)],
203+
),
204+
),
205+
],
206+
/* flags */ ts.NodeFlags.Const,
207+
),
208+
);
209+
}
210+
87211
function transformEnumSchemaObject(schemaObject: EnumSchemaObject, options: TransformNodeOptions) {
88212
// If the `enum` or `enumValues` options are enabled, create and hoist the declarations
89213
// for the enum and array, so long as all enum members are string or number literals.
@@ -152,8 +276,13 @@ function transformEnumSchemaObject(schemaObject: EnumSchemaObject, options: Tran
152276
)
153277
: undefined;
154278

279+
const constTypePredicate =
280+
options.ctx.enumValues && constName && constTypeName
281+
? getTypePredicate(constName, constTypeName, options)
282+
: undefined;
283+
155284
// Add declarations for all selected output types.
156-
for (const node of [enumType, constExpression, constMemberType]) {
285+
for (const node of [enumType, constExpression, constMemberType, constTypePredicate]) {
157286
if (node && !options.ctx.injectNodes.includes(node)) {
158287
options.ctx.injectNodes.push(node);
159288
}

packages/openapi-typescript/test/node-api.test.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -888,18 +888,29 @@ export type operations = Record<string, never>;`,
888888
},
889889
},
890890
},
891-
want: `export const pathsUrlGetParametersQueryStatusValues = ["active", "inactive"] as const;
891+
want: `function get_is<TItem extends number | string>(items: readonly TItem[]) {
892+
return (value: null | number | string | undefined): value is TItem => {
893+
return value !== null && value !== undefined && items.includes(value as TItem);
894+
};
895+
}
896+
export const pathsUrlGetParametersQueryStatusValues = ["active", "inactive"] as const;
892897
export type pathsUrlGetParametersQueryStatus = (typeof pathsUrlGetParametersQueryStatusValues)[number];
898+
export const is_pathsUrlGetParametersQueryStatus = get_is<pathsUrlGetParametersQueryStatus>(pathsUrlGetParametersQueryStatusValues);
893899
export const pathsUrl2GetParametersQueryStatusAnyOf0Values = ["approved", "rejected"] as const;
894900
export type pathsUrl2GetParametersQueryStatusAnyOf0 = (typeof pathsUrl2GetParametersQueryStatusAnyOf0Values)[number];
901+
export const is_pathsUrl2GetParametersQueryStatusAnyOf0 = get_is<pathsUrl2GetParametersQueryStatusAnyOf0>(pathsUrl2GetParametersQueryStatusAnyOf0Values);
895902
export const pathsUrl2GetParametersQueryStatusAnyOf1Values = ["appealed"] as const;
896903
export type pathsUrl2GetParametersQueryStatusAnyOf1 = (typeof pathsUrl2GetParametersQueryStatusAnyOf1Values)[number];
904+
export const is_pathsUrl2GetParametersQueryStatusAnyOf1 = get_is<pathsUrl2GetParametersQueryStatusAnyOf1>(pathsUrl2GetParametersQueryStatusAnyOf1Values);
897905
export const StatusAnyOf0Values = ["active", "inactive"] as const;
898906
export type StatusAnyOf0 = (typeof StatusAnyOf0Values)[number];
907+
export const is_StatusAnyOf0 = get_is<StatusAnyOf0>(StatusAnyOf0Values);
899908
export const StatusAnyOf1Values = ["pending"] as const;
900909
export type StatusAnyOf1 = (typeof StatusAnyOf1Values)[number];
910+
export const is_StatusAnyOf1 = get_is<StatusAnyOf1>(StatusAnyOf1Values);
901911
export const ErrorCodeValues = [100, 101, 102, 103, 104, 105] as const;
902912
export type ErrorCode = (typeof ErrorCodeValues)[number];
913+
export const is_ErrorCode = get_is<ErrorCode>(ErrorCodeValues);
903914
export interface paths {
904915
"/url": {
905916
parameters: {

0 commit comments

Comments
 (0)