Enten Types is a library to create type-safe guards to check the shape of unknown data
npm install @i-doit/enten-types
You can define the shape of the expected data using the type guards.
The following guards are available:
These guards check if an argument has a specific type.
For example,
const data = await fetch('/api/data').then(a => a.json());
if (isNumber(data)) {
// data is a number
}
isObject
checks if an argument is an object.
isObjectWithShape
checks if it's an object with predefined shape. It receives key-value pairs with guards for each field.
isMapOf
checks if it's an associative array with all values matching the passed guard.
For example,
const isApiEntry = isObjectWithShape({
id: isString,
order: isNumber,
active: isBoolean,
});
const data = await fetch('/api/data').then(a => a.json());
if (isApiEntry(data)) {
// data has type
// {
// id: string;
// order: number;
// active: bool;
// }
}
if (isMapOf(isApiEntry)(data)) {
// data has a type Record<string, ApiEntry>
}
isArray
checks if an argument is an array.
isArrayOf
checks if an argument is an array and every element of the array matches the passed guard.
For example,
const data = await fetch('/api/data').then(a => a.json());
if (isArrayOf(isApiEntry)(data)) {
// data has type ApiEntry[]
}
Optional guard allows you to create a guard that allows undefined.
For example,
const isApiEntry = isObjectWithShape({
id: isString,
title: optional(isString),
})
// isApiEntry checks if an argument has the shape:
// {
// id: string;
// title?: string;
// }
isExact checks if the value has exactly the same value as expected.
This can be useful to create the discriminator maps.
const isEqFilter = isObjectWithShape({
operation: isExact<'eq'>('eq'),
value: isString,
field: isString
});
// EqFilter has type
// {
// operation: 'eq';
// value: string;
// field: string;
// }
To retrieve the type that supports guard, you can use GetType
type:
For example,
const isApiEntry = isObjectWithShape({
id: isString,
title: isString,
name: optional(isString),
});
type ApiEntryType = GetType<typeof isApiEntry>;
// ApiEntryType is
// {
// id: string;
// title: string;
// name: string | undefined;
// }
This way you can define your type using the guards.
You can combine guards together using andX or orX:
const isIdentifiable = isObjectWithShape({
id: isString
});
const isUser = andX(isIdentifiable, isObjectWithShape({
username: string
}));
You can combine your guards to create new ones:
const isApiResponse = isObjectWithShape({
offset: isNumber,
total: isNumber,
});
const isUserApiResponse = andX(isApiResponse, isObjectWithShape({
results: isArrayOf(isUser)
}))
type UserApiResponse = GetType<typeof isUserApiResponse>;
npm t
: Run test suitenpm start
: Runnpm run build
in watch modenpm run test:watch
: Run test suite in interactive watch modenpm run test:prod
: Run linting and generate coveragenpm run build
: Generate bundles and typings, create docsnpm run lint
: Lints codenpm run commit
: Commit using conventional commit style (husky will tell you to use it if you haven't 😉)