joi-to-typescript

joi-to-typescript on GitHub
Convert Joi Schemas to TypeScript interfaces
This will allow you to use generate TypeScript interfaces from Joi Schemas giving you confidence the schema and interface match. You no longer have to manually create the same structure again, saving you time and reducing errors.
Works with any TypeScript project and also perfectly with Hapi API requests/responses.
For generating Open Api/Swagger this project works with
The use of .meta({className:'')
is preferred over .label('')
, because Joi.label()
is intended to be used for meaningful error message, using it for another purpose makes Joi lose a standard feature, this is especially noticeable for frontend usages of Joi. The choice of the property className
is because this property is used by joi-to-swagger making this project work with other projects.
Installation Notes
This package is intended as a development time tool, so it should be installed in the devDependencies
yarn add joi-to-typescript --dev
npm install joi-to-typescript --save-dev
You will also need to install joi
in the dependencies
yarn add joi
npm install joi
- This has been built for
"joi": "^17"
and will not work for older versions - Supported node versions 18, 20
Suggested Usage
- Create a Schemas Folder eg.
src/schemas
- Create a interfaces Folder eg.
src/interfaces
- Create Joi Schemas in the Schemas folder with a file name suffix of Schemas eg.
AddressSchema.ts
- The file name suffix ensures that type file and schema file imports are not confusing
Example
Example Project
Explore the Example Projects for recommended setup, execute yarn types
to run each one.
Example Schema
This example can be found in src/__tests__/readme
import Joi from 'joi';
export const JobSchema = Joi.object({
businessName: Joi.string().required(),
jobTitle: Joi.string().required()
}).meta({ className: 'Job' });
export const WalletSchema = Joi.object({
usd: Joi.number().required(),
eur: Joi.number().required()
})
.unknown()
.meta({ className: 'Wallet', unknownType: 'number' });
export const PersonSchema = Joi.object({
firstName: Joi.string().required(),
lastName: Joi.string().required().description('Last Name'),
job: JobSchema,
wallet: WalletSchema
}).meta({ className: 'Person' });
export const PeopleSchema = Joi.array()
.items(PersonSchema)
.required()
.meta({ className: 'People' })
.description('A list of People');
export interface Job {
businessName: string;
jobTitle: string;
}
export type People = Person[];
export interface Person {
firstName: string;
job?: Job;
lastName: string;
wallet?: Wallet;
}
export interface Wallet {
[x: string]: number;
eur: number;
usd: number;
}
Points of Interest
export const PersonSchema
schema must be exportedexport const PersonSchema
includes a suffix of Schema so the schema and interface are not confused when using import
statements (recommended not required).meta({ className: 'Person' });
Sets interface
name using TypeScript conventions (TitleCase Interface name, camelCase property name).meta({ unknownType: 'number' });
assert unknown type to number
Upgrade Notice
- Version 1 used
.label('Person')
as the way to define the interface
name, to use this option set { useLabelAsInterfaceName: true }
Example Call
import { convertFromDirectory } from 'joi-to-typescript';
convertFromDirectory({
schemaDirectory: './src/schemas',
typeOutputDirectory: './src/interfaces',
debug: true
});
import { convertSchema } from 'joi-to-typescript';
const resultingInterface = convertSchema({}, JobSchema);
resultingInterface?.content =
Settings
export interface Settings {
schemaDirectory: string;
typeOutputDirectory: string;
useLabelAsInterfaceName: boolean;
defaultInterfaceSuffix?: string;
defaultToRequired: boolean;
schemaFileSuffix: string;
interfaceFileSuffix: string;
debug: boolean;
fileHeader: string;
sortPropertiesByName: boolean;
flattenTree: boolean;
rootDirectoryOnly: boolean;
indexAllToRoot: boolean;
commentEverything: boolean;
ignoreFiles: string[];
indentationChacters: string;
doublequoteEscape: boolean;
treatDefaultedOptionalAsRequired: boolean;
supplyDefaultsInType: boolean;
supplyDefaultsInJsDoc: boolean;
inputFileFilter: RegExp;
omitIndexFiles: boolean
tsContentHeader?: (type: ConvertedType) => string;
tsContentFooter?: (type: ConvertedType) => string;
unionNewLine?: boolean;
tupleNewLine?: boolean;
}
Joi Features Supported
Contributing
Recommended Development Environment
Recommended Editor is VS Code, this project is setup with VSCode settings in the ./.vscode
directory to keep development consistent.
Best developed on macOS, Linux, or on Windows via WSL.
Install nodejs via nvm so you can have multiple versions installed
nvm use
yarn install
yarn test
yarn coverage
yarn lint
Change Log
See GitHub Releases