Verifying the format of an object received from an HTTP service using a TypeScript interface

Ensuring that the structure of the http JSON response aligns with a typescript interface/type is crucial for our javascript integration tests against the backend.

Take, for example, our CurrentUser interface:

export interface CurrentUser {
  id: number;
  firstname: string;
  lastname: string;
  roles: string[];
}

If a returned object from the backend doesn't include a firstname property but includes an age property, it's essential to detect this mismatch and fail the test at runtime.

We aim to automate these checks without manually coding assertions for each field. However, given that typescript interfaces don't exist at runtime, how can we leverage typescript for such validations?

If achieving this through typescript is unfeasible, are there any alternative libraries or patterns we could explore?

Answer №1

It is not possible to directly use a typescript interface for runtime type assertions. A custom javascript implementation must be created instead.

(i) An unofficial TS compiler exists that can automate this process.

I would like to avoid manually coding checks/assertions for each field.

Unfortunately, unless opting for a custom solution like a unique compiler, manual checks are required. For instance, object testing can be done as shown below:

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);
}

// returns false (missing lastname property)
document.body.innerText = isValidUser({
    id: 1,
    firstname: 'John',
    roles: ['admin', 'content-editor'],
});

The provided example covers basic checks for all properties (presence and type). Moreover, libraries like Lodash and Ramda can simplify the type checking syntax and enhance readability. Here's an illustration:

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)),
]);

// returns false (missing lastname property)
document.body.innerText = isValidUser({
    id: 1,
    firstname: 'John',
    roles: ['admin', 'content-editor'],
});
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="107c7f7471637850243e21273e24">[email protected]</a>/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Modifying the text of a material UI button depending on a particular condition

I have a component that uses the Material UI button, and I need to change the text of the button based on a condition. If the order amount is 0, I want it to display "cancel", otherwise, it should show "refund". Can someone guide me on how to achieve thi ...

Guide on incorporating typed components into module federation in React

I've been encountering an issue with setting the type of a custom component exposed through Webpack module federation. Though I have successfully exposed and used the component, Typescript is flagging an error regarding its type. The situation invol ...

Sliding out the sidebar menu with Jquery

Hey there! I'm currently working on implementing a swipe feature for the side menu bar. I have already added a click method in my code, but now I also need to incorporate a swipe technique. When the side bar is opened, I want to remove the inside im ...

Troubleshooting compatibility issues between jQuery scrollLeft and Angular

As I attempt to programmatically scroll a horizontally overflowing div, I am confident that my final code should resemble the following: var $element = $('#widgetRow');//or maybe $('#widgetRow').parent(); //With 1 or more parent()& ...

Encountering the "RequestDevice() chooser has been cancelled by the user" error when using Electron 17.x with Web Bluetooth

After reviewing the following StackOverflow resources: Web Bluetooth & Chrome Extension: User cancelled the requestDevice() chooser Electron Web Bluetooth API requestDevice() Error Can you manipulate web bluetooth chooser that shows after calling requestD ...

Unable to Achieve Full Height with Vuetify Container

Challenge: I'm facing an issue with my <v-container> component not consistently spanning the entire height of the application. Despite trying various methods such as using the fill-height property, setting height: 100%;, height: 100vh;, and expe ...

What is the best way to trigger a modal to appear when dynamically generated items are clicked?

My objective is to retrieve specific data from the server and present it in a list format on my webpage. The list displays correctly, but I want users to be able to view additional details for each list item by clicking on it, triggering a modal popup. How ...

Using Next.js with Firebase emulators

I've been struggling to configure Firebase's V9 emulators with Next.js, but I keep running into the same error message. See it here: https://i.stack.imgur.com/Uhq0A.png The current version of Firebase I'm using is 9.1.1. This is how my Fir ...

The variable's Ionic value is not being displayed in the HTML

I recently developed a new Ionic application and encountered an issue while attempting to display a variable value in the HTML. Without making any modifications, this is the current state of my page after creating the app. import { IonicModule } from &ap ...

Sending documents via ExpressJS

I'm currently developing a small application using the latest NodeJS and ExpressJS, but I've encountered an issue with uploading files. My routes are set up like this: app.get('/Share', share.index); app.post('/Share/Process&apos ...

Angular 8 does not allow for the assignment of type '{}' to a parameter

I have a unique approach for managing errors: private handleErrors<T>(operation = 'operation', result?: T) { return (error: any): Observable<T> => { console.error(error); this.record(`${operation} failed: ${error.m ...

Is Python a suitable programming language for developing applications on a Raspberry Pi device?

I'm diving into the coding world for the first time and I have a project in mind - controlling my RC car with my smartphone using a Raspberry Pi 3. Research suggests that I should use Node.JS and JavaScript to create the app, but I'm wondering if ...

Vanilla JavaScript Troubleshooting: Top Scroll Button Issue

Attempting to develop a Scroll To Top Button utilizing Vanilla JS, encountering errors in the dev console. Existing jQuery code that needs conversion to vanilla js Uncaught TypeError: Cannot read property 'addEventListener' of null My Vanilla ...

Adding a loading event listener to an object in JavaScript: A step-by-step guide

I'm currently deep into developing a game using sprites in JavaScript. I've been trying to incorporate an event listener that verifies whether the sprite images have loaded before beginning the game. Employing object-oriented programming, I' ...

Attempting to load an image through an AJAX Request may prove to be unsuccessful

I am facing an issue with using an AJAX request to load a gif image from the following source: Despite my attempts, using the response on the image source like this: $('#cat-thumb-1').attr('src', 'data:image/gif;base64,' + d ...

Tips for displaying "No Results" without causing any lag in the browser

I'm encountering difficulty trying to implement a simple feature without resorting to a "messy" solution. The performance is suffering, and I am certain it's not done in a "professional" manner. What I desire is straightforward – displaying a ...

Sort div elements based on checkbox filters

I have a set of checkboxes that I want to use for filtering purposes. <div class="filter"> <div class="checkbox"> <label><input type="checkbox" rel="canada"/>Canada</label> </div> <div class="chec ...

You were supposed to provide 2 arguments, but you only gave 1.ts(2554)

Hey everyone, I hope you're having a good morning. Apologies for the inconvenience, I've been practicing to improve my skills and encountered an issue while working on a login feature. I'm trying to connect it to an API but facing a strange ...

Can VueJS lifecycle hooks be outsourced?

Is it possible to organize lifecycle hooks (like created / mounted) in a separate file for better simplicity and cleanliness? MyGreatView.vue import created from 'created.js' export default { created, // created() { console.log('vue Cre ...

What is the process for moving information between files?

I have two files which are named as, employee-rates-controller.ts: private load() { return this.entityService .load(this.$scope.projectRevisionUid) .then(resp => { localStorage.removeItem('employeerates'); this.$ ...