Skip to content

Commit 791ce53

Browse files
committed
Export type-predicates along when enumValues is true
1 parent 0abed11 commit 791ce53

File tree

2 files changed

+146
-2
lines changed

2 files changed

+146
-2
lines changed

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

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

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

154287
// Add declarations for all selected output types.
155-
for (const node of [enumType, constExpression, constMemberType]) {
288+
for (const node of [enumType, constExpression, constMemberType, constTypePredicate]) {
156289
if (node && !options.ctx.injectNodes.includes(node)) {
157290
options.ctx.injectNodes.push(node);
158291
}

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -855,18 +855,29 @@ export type operations = Record<string, never>;`,
855855
},
856856
},
857857
},
858-
want: `export const pathsUrlGetParametersQueryStatusValues = ["active", "inactive"] as const;
858+
want: `function get_is<TItem extends number | string>(items: readonly TItem[]) {
859+
return (value: null | number | string | undefined): value is TItem => {
860+
return value !== null && value !== undefined && items.includes(value as TItem);
861+
};
862+
}
863+
export const pathsUrlGetParametersQueryStatusValues = ["active", "inactive"] as const;
859864
export type pathsUrlGetParametersQueryStatus = (typeof pathsUrlGetParametersQueryStatusValues)[number];
865+
export const is_pathsUrlGetParametersQueryStatus = get_is<pathsUrlGetParametersQueryStatus>(pathsUrlGetParametersQueryStatusValues);
860866
export const pathsUrl2GetParametersQueryStatusAnyOf0Values = ["approved", "rejected"] as const;
861867
export type pathsUrl2GetParametersQueryStatusAnyOf0 = (typeof pathsUrl2GetParametersQueryStatusAnyOf0Values)[number];
868+
export const is_pathsUrl2GetParametersQueryStatusAnyOf0 = get_is<pathsUrl2GetParametersQueryStatusAnyOf0>(pathsUrl2GetParametersQueryStatusAnyOf0Values);
862869
export const pathsUrl2GetParametersQueryStatusAnyOf1Values = ["appealed"] as const;
863870
export type pathsUrl2GetParametersQueryStatusAnyOf1 = (typeof pathsUrl2GetParametersQueryStatusAnyOf1Values)[number];
871+
export const is_pathsUrl2GetParametersQueryStatusAnyOf1 = get_is<pathsUrl2GetParametersQueryStatusAnyOf1>(pathsUrl2GetParametersQueryStatusAnyOf1Values);
864872
export const StatusAnyOf0Values = ["active", "inactive"] as const;
865873
export type StatusAnyOf0 = (typeof StatusAnyOf0Values)[number];
874+
export const is_StatusAnyOf0 = get_is<StatusAnyOf0>(StatusAnyOf0Values);
866875
export const StatusAnyOf1Values = ["pending"] as const;
867876
export type StatusAnyOf1 = (typeof StatusAnyOf1Values)[number];
877+
export const is_StatusAnyOf1 = get_is<StatusAnyOf1>(StatusAnyOf1Values);
868878
export const ErrorCodeValues = [100, 101, 102, 103, 104, 105] as const;
869879
export type ErrorCode = (typeof ErrorCodeValues)[number];
880+
export const is_ErrorCode = get_is<ErrorCode>(ErrorCodeValues);
870881
export interface paths {
871882
"/url": {
872883
parameters: {

0 commit comments

Comments
 (0)