I would like to check that the content/structure of the http JSON response does indeed match a typescript interface/type. This is in the context of javascript integration tests against a running backend.
For instance one of our typescript interfaces is as follows:
export interface CurrentUser {
id: number;
firstname: string;
lastname: string;
roles: string[];
}
If the backend returns an object that contains an age property and no firstname property, then I would like somehow to be notified at runtime that the structure of the returned object differs and the test would fail.
Also, I want to avoid having to manually code a check/assertion for each of the fields.
I understand typescript interfaces no longer exist at runtime. Then how can I rely on typescript for such checks?
If that is not possible with typescript, then what would be an alternative (library, pattern)?
There is no native way to use a typescript interface directly to perform runtime type assertions. You should create your own javascript implementation instead.
(i) There is an unofficial TS compiler that allows for automating this process.
Also, I want to avoid having to manually code a check/assertion for each of the fields.
Unfortunately, unless you decide to go with a solution such as a custom compiler you'll have to. For example, you could test objects like this:
const isValidUser = (object) => {
if ('object' !== typeof object) return false;
const { id, firstname, lastname, roles } = object;
return typeof id === 'number'
&& typeof firstname === 'string'
&& typeof lastname === 'string'
&& Array.isArray(roles)
&& roles.reduce((acc, value) => acc && typeof value === 'string', true);
}
// prints false (missing lastname property)
document.body.innerText = isValidUser({
id: 1,
firstname: 'John',
roles: ['admin', 'content-editor'],
});
The example above performs all basic checks for all properties (presence and type). Additionally, you could use libraries like Lodash and Ramda to clean up the type checking syntax, and make things a little easier to understand (and maintain). An example:
const { all, allPass, pipe, prop } = R;
const { isArray, isNumber, isObject, isString } = _;
const isValidUser = allPass([
isObject,
pipe(prop('id'), isNumber),
pipe(prop('firstname'), isString),
pipe(prop('lastname'), isString),
pipe(prop('roles'), isArray),
pipe(prop('roles'), all(isString)),
]);
// prints false (missing lastname property)
document.body.innerText = isValidUser({
id: 1,
firstname: 'John',
roles: ['admin', 'content-editor'],
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With