Skip to content

Commit 1064b93

Browse files
committed
refactor(guards): extracted to helper functions
1 parent 96ce207 commit 1064b93

File tree

3 files changed

+84
-70
lines changed

3 files changed

+84
-70
lines changed

src/helper.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Reference } from 'swagger-schema-official';
2-
import { FileInfix } from './types';
2+
import { FileInfix, Property } from './types';
33

44
export const BASIC_TS_TYPE_REGEX = /\b(?:string|number|integer|bigint|boolean|object|void)\b/;
55
const BUILD_IN_TS_TYPE_REGEX = /^(?:string|number|integer|boolean|null|undefined|any|void|Object|File|Blob)\b/i;
@@ -56,7 +56,7 @@ export function dereferenceType(refString: string | undefined): string {
5656
*
5757
* example: shipmentShipmentAddress --> ShipmentAddress
5858
*
59-
* note: minimum is 3 letters otherwise words are not striped
59+
* note: minimum is 3 letters otherwise words are kept in place
6060
*
6161
* @param {string} text
6262
* @returns {string}
@@ -90,6 +90,53 @@ export function toTypescriptType(type: string | undefined): string {
9090
return typeName(type);
9191
}
9292

93+
export function accessProp(arg: string): string {
94+
return arg.startsWith("'") ? `arg[${arg}]` : `arg.${arg}`;
95+
}
96+
97+
function guardArray(prop: Property): string {
98+
return `(Array.isArray(${accessProp(prop.name)}) && ${accessProp(
99+
prop.name,
100+
)}.every((item: unknown) => ${
101+
prop.isPrimitiveType
102+
? `typeof item === '${prop.type}'`
103+
: prop.typescriptType && prop.typescriptType.endsWith('[]') // checks if item is nested array type
104+
? `(Array.isArray(item) && item.every((itemItem: unknown) => ${(prop.isPrimitiveType
105+
? `typeof itemItem === '${prop.type}'`
106+
: `is${prop.typescriptType}(itemItem)`
107+
).replace('[]', '')}))`
108+
: `is${prop.typescriptType}(item)`
109+
}))`;
110+
}
111+
112+
// text?: ItemList & Data
113+
// ( typeof arg.text === 'undefined' || ( typeof arg['0'] === 'undefined' || isItemList(arg.text) && typeof arg['1'] === 'undefined' || isData(arg.text) ) ) &&
114+
115+
export function guardFn(fn: () => string, prop: Property): string {
116+
return `
117+
${
118+
prop.isRequired
119+
? ''
120+
: `typeof ${accessProp(prop.name)} === 'undefined' ||`
121+
}
122+
${
123+
prop.name === ADDITIONAL_PROPERTIES_KEY
124+
? `Object.values(arg).every((item: unknown) => ${
125+
prop.isPrimitiveType
126+
? `typeof item === '${prop.type}'`
127+
: `is${prop.typescriptType}(item)`
128+
})`
129+
: prop.isArray
130+
? guardArray(prop)
131+
: `${
132+
prop.isPrimitiveType
133+
? `typeof ${accessProp(prop.name)} === '${prop.type}'`
134+
: fn()
135+
}`
136+
}
137+
`;
138+
}
139+
93140
export function typeName(
94141
name: string = 'any',
95142
isArray: boolean = false,

src/parser.ts

Lines changed: 33 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
compareStringByKey,
3131
isReference,
3232
ADDITIONAL_PROPERTIES_KEY,
33+
accessProp,
34+
guardFn,
3335
} from './helper';
3436

3537
interface Parameters {
@@ -325,7 +327,7 @@ function parseInterfaceProperties(
325327
): Property[] {
326328
return Object.entries<Schema>(properties)
327329
.map(([propName, propSchema]: [string, Schema]) => {
328-
const isArray = /^array$/i.test(propSchema.type || '');
330+
const isArray = propSchema.type === 'array';
329331
const ref =
330332
propSchema.additionalProperties &&
331333
typeof propSchema.additionalProperties !== 'boolean'
@@ -364,86 +366,48 @@ function parseInterfaceProperties(
364366
);
365367
const allOfImports = propertyAllOf.map(prop => `${prop.typescriptType}`);
366368
const type = typescriptType.replace('[]', '');
367-
const isPrimitiveType = BASIC_TS_TYPE_REGEX.test(type);
368369
const name =
369370
/^[A-Za-z_$][\w$]*$/.test(propName) ||
370371
propName === ADDITIONAL_PROPERTIES_KEY
371372
? propName
372373
: `'${propName}'`;
373-
const isRequired = requiredProps.includes(propName);
374-
375-
const accessProp = (arg: string): string =>
376-
arg.startsWith("'") ? `arg[${arg}]` : `arg.${arg}`;
377-
378-
const guardFn = (fn: () => string, prop: Property = {}) => `
379-
${
380-
prop.hasOwnProperty('isRequired') || isRequired
381-
? ''
382-
: `typeof ${accessProp(name)} === 'undefined' ||`
383-
}
384-
${
385-
prop.isArray || isArray
386-
? `(Array.isArray(${accessProp(name)}) && ${accessProp(
387-
name,
388-
)}.every((item: unknown) => ${
389-
prop.isPrimitiveType || isPrimitiveType
390-
? `typeof item === '${prop.type || type}'`
391-
: (prop.typescriptType || typescriptType).endsWith('[]') // checks if item is nested array type
392-
? `(Array.isArray(item) && item.every((itemItem: unknown) => ${(prop.isPrimitiveType ||
393-
isPrimitiveType
394-
? `typeof itemItem === '${prop.type || type}'`
395-
: `is${prop.typescriptType || typescriptType}(itemItem)`
396-
).replace('[]', '')}))`
397-
: `is${prop.typescriptType || typescriptType}(item)`
398-
}))`
399-
: name === ADDITIONAL_PROPERTIES_KEY
400-
? `Object.values(arg).every((item: unknown) => ${
401-
isPrimitiveType
402-
? `typeof item === '${type}'`
403-
: `is${typescriptType}(item)`
404-
})`
405-
: `${
406-
prop.isPrimitiveType || isPrimitiveType
407-
? `typeof ${accessProp(prop.name || name)} === '${prop.type ||
408-
type}'`
409-
: fn()
410-
}`
411-
}
412-
`;
413-
414-
const guard = `(${guardFn(() =>
415-
propertyAllOf.length
416-
? `(${propertyAllOf
417-
.map(prop =>
418-
guardFn(
419-
() => `is${prop.typescriptType}(${accessProp(name)})`,
420-
prop,
421-
),
422-
)
423-
.join(' && ')})`
424-
: 'enum' in propSchema
425-
? `[${(typescriptType || '').replace(
426-
/ \| /g,
427-
', ',
428-
)}].includes(${accessProp(name)})`
429-
: `is${typescriptType}(${accessProp(name)})`,
430-
).replace(/\s+/g, ' ')}) &&`;
431374

432-
return {
375+
const property: Property = {
433376
isArray,
434-
isDictionary: propSchema.additionalProperties,
377+
isDictionary: !!propSchema.additionalProperties,
435378
isRef,
436-
isPrimitiveType,
437-
isRequired,
379+
isPrimitiveType: BASIC_TS_TYPE_REGEX.test(type),
380+
isRequired: requiredProps.includes(propName),
438381
name,
439382
description: replaceNewLines(propSchema.description),
440383
type: type,
441-
guard,
442384
typescriptType: allOfParsed.length
443385
? allOfParsed.join(' & ')
444386
: typescriptType,
445387
imports: isRef ? [type, ...allOfImports] : allOfImports,
446388
};
389+
390+
const guard = `(${guardFn(
391+
() =>
392+
propertyAllOf.length
393+
? `(${propertyAllOf
394+
.map(prop =>
395+
guardFn(
396+
() => `is${prop.typescriptType}(${accessProp(name)})`,
397+
{ ...prop, name, isRequired: true },
398+
),
399+
)
400+
.join(' && ')})`
401+
: 'enum' in propSchema
402+
? `[${(typescriptType || '').replace(
403+
/ \| /g,
404+
', ',
405+
)}].includes(${accessProp(name)})`
406+
: `is${typescriptType}(${accessProp(name)})`,
407+
property,
408+
).replace(/\s+/g, ' ')}) &&`;
409+
410+
return { ...property, guard };
447411
})
448412
.sort(compareStringByKey('name')); // tslint:disable-line:no-array-mutation
449413
}
@@ -617,7 +581,8 @@ function transformParameters(
617581
const paramRef: Partial<SwaggerParameter> = derefName
618582
? allParams[derefName] || {}
619583
: {};
620-
const name = 'name' in paramRef ? paramRef.name : (param as Parameter).name;
584+
const name =
585+
'name' in paramRef ? paramRef.name || '' : (param as Parameter).name;
621586
const type =
622587
('type' in param && param.type) ||
623588
(paramRef && 'type' in paramRef && paramRef.type) ||
@@ -660,7 +625,8 @@ function transformParameters(
660625
isArray,
661626
isRequired:
662627
(param as Parameter).isRequired ||
663-
(param as { required: boolean }).required ||
628+
// tslint:disable-next-line:no-any
629+
(<any>param).required ||
664630
paramRef.required,
665631
name,
666632
typescriptType,

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ export interface Property {
4848
readonly isArray?: boolean;
4949
readonly isRef?: boolean;
5050
readonly isPrimitiveType?: boolean;
51+
readonly isDictionary?: boolean;
5152
readonly in?: In | string;
5253
readonly enum?: (string | boolean | number | {})[];
5354
readonly items?: Schema | Schema[];
54-
readonly name?: string;
55+
readonly name: string;
5556
readonly description?: string;
5657
readonly $ref?: string;
5758
readonly schema?: Schema;

0 commit comments

Comments
 (0)