joi-extract-type
Version:
Provides native type extraction from Joi schemas for Typescript
304 lines (265 loc) • 10.7 kB
text/typescript
/** @format */
import * as Joi from '@hapi/joi';
import './index';
const extJoi = Joi.extend({} as Joi.Extension);
// Unknown types or AnySchema defaults to type any
const any_schema = Joi.any();
type extractAny = Joi.extractType<typeof any_schema>;
export const extractedAny: extractAny = 'anything';
const is_enabled = Joi.boolean();
type extractBoolean = Joi.extractType<typeof is_enabled>;
export const extractedBoolean: extractBoolean = true;
const full_name = extJoi.string();
type extractString = Joi.extractType<typeof full_name>;
export const extractedString: extractString = 'string';
const created_at = Joi.date();
type extractDate = Joi.extractType<typeof created_at>;
export const extractedDate: extractDate = new Date();
const priority = extJoi.number();
type extractNumber = Joi.extractType<typeof priority>;
export const extractedNumber: extractNumber = 5;
const userAsObject = {
full_name: full_name.required(),
short_desc: extJoi.string(),
is_enabled,
has_credentials: Joi.boolean().valid(true).required(),
created_at,
priority,
};
const user = Joi.object(userAsObject);
type extractObject = Joi.extractType<typeof userAsObject>;
type extractObjectSchema = Joi.extractType<typeof user>;
export const extractedObject: extractObject = {
created_at: extractedDate,
full_name: extractedString,
is_enabled: extractedBoolean,
priority: extractedNumber,
has_credentials: true,
};
export const extractedObjectSchema: extractObjectSchema = extractedObject;
const roles = extJoi
.array()
.items(extJoi.string().valid(['admin', 'member', 'guest']))
.items(extJoi.number());
type extractArray = Joi.extractType<typeof roles>;
export const extactedArray: extractArray = ['admin', 2];
const uuid_exp = `[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`;
const uuid_pattern = new RegExp(uuid_exp, 'i');
const uuid = extJoi.string().regex(uuid_pattern);
type extractUuid = Joi.extractType<typeof uuid>;
export const extractedUuid: extractUuid = '123e4567-e89b-12d3-a456-426655440000';
const apply = extJoi.array().items(Joi.object({ id: uuid.required() }));
type extractApply = Joi.extractType<typeof apply>;
const anyApply = [{ id: '3' }, { id: undefined }];
export const extractedApply: extractApply = anyApply;
const rule_flat = extJoi
.array()
.items(extJoi.string())
.items(Joi.object({ id: uuid.required() }));
type extractRuleFlat = Joi.extractType<typeof rule_flat>;
export const extractedRuleFlat: extractRuleFlat = [{ id: 'string' }, 'test'];
const rule = Joi.object().keys({ apply, id: uuid.required() });
type extractRule = Joi.extractType<typeof rule>;
export const extractedRule: extractRule = {
apply: extractedApply,
id: 'string',
};
export const ruleMap = Joi.object().pattern(/\w+/, rule);
type extractRuleMap = Joi.extractType<typeof ruleMap>;
export const extractedRuleMap: extractRuleMap = { somekey: extractedRule };
export const jobOperatorRoleSchema = Joi.object({
id: extJoi.string().required(),
user_id: extJoi.string().required(),
job_id: extJoi.string(),
index: extJoi.number(),
parent_index: extJoi.number().required(),
role: extJoi.string().valid('recruiter', 'requester'),
pipeline_rules: extJoi.array().items(rule).required(),
});
type extractComplexType = Joi.extractType<typeof jobOperatorRoleSchema>;
export const extractedComplexType: extractComplexType = {
id: '2015',
user_id: '102',
job_id: '52',
role: 'requester',
parent_index: 5,
pipeline_rules: [extractedRule],
};
export const usingDefaultWithProps = Joi.object({
number_prop_with_default: extJoi.number().default(2),
string_prop_with_default: extJoi.string().default('string'),
boolean_prop_with_default: Joi.boolean().default(false),
date_prop_with_default: Joi.date().default(new Date()),
array_prop_with_default: extJoi.array().default([5]),
object_prop_with_default: Joi.object({ number: extJoi.number() }).default({
string: extJoi.string(),
}),
});
type usingDefaultWithPropsType = Joi.extractType<typeof usingDefaultWithProps>;
export const extractedUsingDefaultWithProps: usingDefaultWithPropsType = {
number_prop_with_default: 20,
string_prop_with_default: 'string',
boolean_prop_with_default: true,
date_prop_with_default: new Date(),
array_prop_with_default: [1, 2],
object_prop_with_default: { number: 5 },
};
export const extractedComplexTypeValidationResponse = Joi.validate({}, jobOperatorRoleSchema);
const appendedJobOperatorRoleSchema = jobOperatorRoleSchema.append({
excluded: Joi.boolean(),
});
type extractAppendedSchema = Joi.extractType<typeof appendedJobOperatorRoleSchema>;
export const extractedAppended: extractAppendedSchema = {
...extractedComplexType,
excluded: true,
};
function someFunction() {
return someFunction;
}
const createUserSchema = Joi.func<typeof someFunction>();
type extractFunction = Joi.extractType<typeof createUserSchema>;
export const extractedFunction: extractFunction = someFunction;
const number_string = Joi.alt().try(extJoi.number().valid(1, 2, 3), extJoi.string());
type extractNumberString = Joi.extractType<typeof number_string>;
export const extractNumberStringNumber: extractNumberString = 2;
export const extractNumberStringString: extractNumberString = '2';
const date_time1 = Joi.alternatives([Joi.date(), extJoi.number(), extJoi.string()]);
type extractDateTime1 = Joi.extractType<typeof date_time1>;
export const extractDateTimeDate1: extractDateTime1 = new Date();
export const extractDateTimeTime1: extractDateTime1 = +new Date();
export const extractDateTimeString1: extractDateTime1 = new Date().toISOString();
const when = Joi.alt().when('x', { is: 'a', then: extJoi.string(), otherwise: extJoi.number() });
type extractedWhen = Joi.extractType<typeof when>;
export const extractWhen1: extractedWhen = 2;
export const extractWhen2: extractedWhen = '2';
const required_alt = Joi.object({
required: Joi.object({
start_date: date_time1.required(),
end_date: date_time1,
value: when.required(),
}).required(),
});
type extractedRequiredAlt = Joi.extractType<typeof required_alt>;
export const extractRequiredAlt: extractedRequiredAlt = {
required: { start_date: new Date(), value: '2' },
};
const required_alt_augmented = required_alt.keys({
required2: Joi.object({
value: extJoi.number(),
})
.required()
.pattern(/\w+/, extJoi.number())
.pattern(extJoi.string(), extJoi.number())
.pattern(extJoi.string().valid('pattern_key'), extJoi.number()),
});
type extractedRequiredAltAugmented = Joi.extractType<typeof required_alt_augmented>;
export const extractRequiredAltAugmented: extractedRequiredAltAugmented = {
required: {
start_date: new Date(),
value: 2,
},
required2: {},
};
const string_array_schema = [
extJoi.string().default('test' as 'test'),
extJoi.array().items([extJoi.string(), extJoi.number()]).valid('string', 2), // TODO overwrite valid on ArraySchema
];
type extractStringArray = Joi.extractType<typeof string_array_schema>;
export const extractStringArrayString: extractStringArray = 'string';
export const extractStringArrayArray: extractStringArray = ['string', 2];
// A extends B type guard
type numberExtendsAny = Joi.extendsGuard<any, extractNumber>;
export const asNumber: numberExtendsAny = 2;
type stringNotNumber = Joi.extendsGuard<string, extractNumber>;
export const asString: stringNotNumber = 'string';
// Validation methods
type priorityValidationResponse = { error: any; value: extractNumber };
export const validationExtractedNumber1: priorityValidationResponse = Joi.validate(
extractedNumber,
priority
);
export const validationExtractedNumber2: priorityValidationResponse = Joi.validate(
extractedNumber,
priority,
{}
);
export const validatedNumber: number =
validationExtractedNumber2.value || validationExtractedNumber1.value;
type strictEnum = 'tag';
export const validationOverwrittenReturn: strictEnum = Joi.validate(
extractedNumber,
priority,
(_err, value: extractNumber) => {
if (typeof value === 'number') return 'tag' as 'tag';
}
);
const nullableString = extJoi.string().valid('string').allow(null, 0);
type extractNullableString = Joi.extractType<typeof nullableString>;
export const extactedNullableStringNull: extractNullableString = null;
export const extactedNullableStringZero: extractNullableString = 0;
export const extactedNullableString: extractNullableString = 'string';
const nullableNumber = extJoi
.number()
.allow(null as null)
.default(5);
type extractNullableNumber = Joi.extractType<typeof nullableNumber>;
export const extactedNullableNumberNull: extractNullableNumber = null;
export const extactedNullableNumber: extractNullableNumber = 15;
const nullType = null as null;
const allNullable = {
nullableString,
nullableNumber,
nullableBoolean: Joi.boolean().required().allow(nullType),
nullableFunc: Joi.func().required().allow(nullType),
nullableDate: Joi.date().required().allow(nullType),
nullableArray: extJoi.array().items(extJoi.string()).allow(nullType),
nullableObject: Joi.object({ nullableString }).allow(nullType),
nullableAlt: Joi.alt(nullableString, nullableNumber).allow(nullType),
};
type extractAllNullable = Joi.extractType<typeof allNullable>;
export const extractedAllNullable: extractAllNullable = {
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableFunc: null,
nullableDate: null,
nullableArray: null,
nullableObject: null,
nullableAlt: null,
};
export const anyRequired = Joi.any().required();
export const anyOptional = Joi.any();
export const anyTestSchema = { anyRequired, anyOptional };
type extractedAnyTest = Joi.extractType<typeof anyTestSchema>;
export let anyTests: extractedAnyTest;
anyTests = { anyRequired: 'test', anyOptional: 'test' };
anyTests = { anyRequired: 'test' };
anyTests = { anyRequired: 123, anyOptional: 123 };
// Test for issue #27
const nestedObjSchema = Joi.object()
.required()
.keys({
nested: Joi.object().required().keys({
foo: Joi.string().required(),
}),
opt: Joi.object().keys({
bar: Joi.string().required(),
}),
});
type extractNestedObjSchema = Joi.extractType<typeof nestedObjSchema>;
export const extractedNestedObjSchema: extractNestedObjSchema = {
nested: {
foo: 'string',
},
};
// Test for issue #36
export const stdSchemaMethods = [
Joi.any().tags('tags').label('any'),
Joi.string().tags('tags').label('string'),
Joi.boolean().tags('tags').label('boolean'),
Joi.number().tags('tags').label('number'),
Joi.date().tags('tags').label('date'),
Joi.object().tags('tags').label('object'),
Joi.array().tags('tags').label('array'),
Joi.alt().tags('tags').label('alt'),
];