Make sure all necessary fields are present in any given input object

I implement TypeScript within AWS Lambdas, where input objects can vary. To ensure that all required fields are present in the input, I have to validate and provide a clear error message specifying any missing fields. If all necessary fields are provided, I then convert the object into a domain object outlined by an interface. Below is an example demonstrating this process:

interface Car {
  wheels: string
  doors: string
  engine: string
}

export const checkIfCar = (maybeCar: Partial<Car>): Car | { error: string } => {
  if (maybeCar.wheels == null) {
    return { error: 'Car should have wheels' };
  } else {
    if (maybeCar.doors == null) {
      return { error: 'Car should have doors' };
    } else {
      if (maybeCar.engine == null) {
        return { error: 'Car should have an engine' }
      } else {
        const car: Car = {
          wheels: maybeCar.wheels,
          doors: maybeCar.doors,
          engine: maybeCar.engine
        }
        return car
      }
    }
  }
}

Although functional, the current method involves significant nesting and redundancy. Is there a more concise TypeScript-native approach to achieve the same functionality? Alternatively, is there a library available that streamlines this pattern? This seems to be a common challenge faced not only by myself.

I am familiar with User defined type guards, but maintaining these guards for each new field added may lead to oversight. Besides, it doesn't effectively address identifying missing fields. I seek a solution that automatically verifies the existence of all object fields.

Answer №1

When it comes to TypeScript, there is no built-in support for runtime type checking or validation. A simple !== null check may not suffice for all scenarios - what if the value is undefined? Or what if the variable is expected to be an object instead of a string?

Fortunately, there are various third-party libraries available that can help with runtime validation and seamlessly integrate with TypeScript. One such library worth mentioning is runtypes. Here is an example of how you can use it:

import * as runtypes from "runtypes";

const CarType = runtypes.Record({
  wheels: runtypes.String,
  doors: runtypes.String,
  engine: runtypes.String,
});

type Car = runtypes.Static<typeof CarType>;

With this setup, you can now perform validation using CarType.check(maybeCar).

Other alternatives to consider include zod, io-ts, and class-validator, each offering their unique approach to achieving similar functionality.

For more intricate validation tasks, combining JSON schema with tools like json-schema-to-typescript and validators like AJV can provide a comprehensive solution that merges validation with static typing.

Answer №2

Here is a potential solution utilizing an automated verification approach.

Regrettably, you must input the keys of the properties to be verified.

snippet

interface Car {
  wheel: string;
  door: string;
  engine: string;
}

const CarKeys = [
  'wheel',
  'door',
  'engine',
];

type ErrorRet = { 
  error: string;
};

const checkIfCar = (possiblyCar: {
  [key in keyof Car]?: string | null;
}): Car | ErrorRet => {
  return CarKeys.reduce((tempRes, x) => {
    const key: keyof Car = x as keyof Car;

    // Terminates if an error has already been identified
    if ('error' in tempRes) {
      return tempRes;
    }
    
    // Verifies for missing data
    if (possiblyCar[key] === void 0 || possiblyCar[key] === null) {
      return {
        error: `Car should include ${x}`,
      };
    }

    tempRes[key] = possiblyCar[key] as string;

    return tempRes;
  }, {} as (Car | ErrorRet));
}

console.log(checkIfCar({
  wheel: '',
  door: '',
}));

console.log(checkIfCar({
  wheel: '',
  door: null,
  engine: '',
}));

console.log(checkIfCar({
  wheel: '',
  door: '',
  engine: '',
}));

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

The challenges of type verification in Redux reducer

I'm currently facing two specific challenges with Typescript and the Redux reducer. Reducer: const defaultState = { selectedLocation: { id: 0, name: 'No Location' }, allLocations: [{ id: 0, name: 'No Location' }], sele ...

The Angular routing system remains constant and does not alter

I'm encountering an issue with loading a component inside another component. Despite the browser route changing, my component fails to update or display properly... Here is my current setup: --homepage --coins-- --coinspreview <-- I am una ...

Create a fresh array by merging two existing arrays together

My project involves working with two separate arrays. The first array contains normal date values: var = [ "2022-05-01", "2022-05-02", ... "2022-05-30" ] The second array consists of objects that contain s ...

TypeScript: The capability to deduce or derive values for a type from a constant object literal that is nested with non-distinct keys

I'm trying to figure out a way to utilize TS/IDE to display specific literal values from a style guide object, instead of just the inferred type. Here's an example: const guide = { colors: { black: { medium: "#000000", } ...

encryption storage for React Native with TypeScript and Redux

// store.ts export const createAppReducer = () => combineReducers({ system: systemReducer, posts: postsReducer, }); const reducer = createAppReducer(); export type RootState = ReturnType<typeof reducer>; export const createAppStore = ...

Encountering a service call error during the execution of Angular 2 unit tests using Jasmine

Struggling with testing an angular service, my TypeScript code seems correct but I keep getting an error in my test cases for "this.someFunction.show()" not being a function, even though it exists in my actual service. Here is what I'm attempting to d ...

The name attribute is not accepted by the HTMLInputElement forwardRef

Check out my project on Codesandbox. Take a look at the input component I've created: import styled from 'styled-components' import React, { forwardRef } from 'react' type Props = { err: boolean maxWidth: string } export co ...

Utilize multiple onChange events in React - one for validating user input and another for formatting the input

I'm looking for a solution to format a Phone number field as (123) 456-7890. Currently, I am using a common handleChange function to handle all input onChange events and JOI for validation. Can someone please assist me with this? import React from &qu ...

Extending a Typescript interface to include the functionality of two different component

When implementing this code snippet, a user will find an output of an <input> element, which works flawlessly: interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { error?: FieldError; icon?: string; id: string; reg ...

Using Typescript: Including an additional argument

While experimenting with the code provided in the documentation interface Point { x: number; y: number; } function getX(p: Point) { return p.x; } class CPoint { x: number; y: number; constructor(x: number, y: num ...

Issue 2339: Dealing with || and Union Types

I've encountered an interesting issue while working with TypeScript and JavaScript. I created a code snippet that runs perfectly in JavaScript but throws a syntax error in TypeScript. You can check it out in action at this TypeScript Sandbox. Essenti ...

Before the service call finishes, Akita queries this.selectAll and returns an empty list

Here is the code snippet from my file named widgetquery.ts: @Injectable({ providedIn: 'root' }) export class WidgetQuery extends QueryEntity<WidgetState, WidgetTO> { public Widget$: Observable<WidgetTO> = this.selectActive().filter( ...

Execute TypeScript on the Angular project's specified version

Is there a way to efficiently manage multiple projects on the same computer that require different versions of Angular? Can I specify the version of Angular within the package.json file to avoid conflicts? ...

Modify a attribute using event handlers from the main template

I've been experimenting with designing components using FASTElement. But I've hit a roadblock trying to pass a dynamic property from parent to child component. Here's a snippet of my design, featuring two FAST templates: childCompTemplate.t ...

Having trouble with Angular 17 router.navigate not functioning properly?

I'm facing an issue with my angular application where I have two pages - one with a form and the other with data. Upon clicking a button, I navigate to the results page using router.navigate. However, I've noticed that sometimes the navigation do ...

Identifying data types in arrays using TypeScript type predicates

My current approach involves a function that validates if a variable is an object and not null: function isRecord(input: any): input is Record<string, any> { return input !== null && typeof input === 'object'; } This type predica ...

Performing an Axios POST request in a React Native and React app using JSON.stringify and Blob functionality

I am currently developing an application where I have encountered an issue when calling an API endpoint in react native. Interestingly, the web app (built with React) does not encounter any errors. Here is the code for the web app using React with TypeScri ...

Error: Attempting to access the 'createClient' property of an undefined object - TypeError

Recently, I've been working on a project where I needed to create and store a session in redis. To achieve this, I referred to the API documentation provided by this GitHub repository https://github.com/tj/connect-redis. However, I encountered an iss ...

changing a one-dimensional array into a two-dimensional array using TypeScript

I need help transforming a flat array into a 2D array, like this: ['-', '-', '-', '-', '-', '-', '-', '-', '-'] My desired output is: [ ['-', '-&apo ...

What is preventing Angular from letting me pass a parameter to a function in a provider's useFactory method?

app.module.ts bootstrap: [AppComponent], declarations: [AppComponent], imports: [ CoreModule, HelloFrameworkModule, ], providers: [{ provide: Logger, useFactory: loggerProviderFunc(1), }] ...