Determining data types through type guarding in Typescript

interface A = {
  name: string;
  ...
};

interface B = {
  name: string;
  ...
};

interface C = {
  key: string;
  ...
};

type UnionOfTypes = A | B | C | ...;

function hasName(item: UnionOfTypes) {
  if ("name" in item) {
    item; // typescript knows here that item is either A or B
  }
}

Can I automatically infer types like if("name" in item) does? My code only uses interfaces/types and not classes.

This way I wouldn't have to explicitly define

function hasName(item: UnionOfTypes): item is A | B {
  ...
}

I'm thinking of using other type guards later on, or are there reasons why narrowing down types this way should be avoided?

Answer №1

There isn't built-in support for this in TypeScript. However, you can create a custom helper function to implement type guarding. This function takes another function as input which returns either the guarded item or false.

function guard<T extends X, X>(fn: (value: X) => T | false)  {
    return function (value: X) : value is T {
        return fn(value) !== false;
    }
}

interface Person {
  name: string;
  age: number;
};

interface Animal {
  type: string;
};

type UnionOfTypes = Person | Animal;

const hasNameProperty = guard(function (item: UnionOfTypes) {
  if ("name" in item) {
    return item;
  }
  return false;
})

// Can be used with other cases as well
let stringsOrNull: Array<string | null> = ["hello", null];
let result = stringsOrNull.filter(guard(v => v ?? false));
let result2 = stringsOrNull.filter(guard(v => v ?? false));

Playground Link

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

When the file is active on a local machine, the bot commands run smoothly. However, these commands do not execute on a remote

Lately, while working on coding a discord bot using discord.js, I came across an issue. Whenever I run my bot on my local machine, all the commands work perfectly fine. However, after committing and pushing the code to GitHub, and then allowing buddy.works ...

Replicating entities in TypeScript

I am currently developing an Angular 2 application using TypeScript. In a User Management component, I have implemented a table that displays all the users in my system. When a user is clicked on within the table, a form appears with their complete set of ...

Issue found in the file assert.d.ts located in the node_modules directory: Expected '{' or ';' at line 3, character 68. Error code: TS1144

When attempting to start the angular application with ng serve, I encountered an error. Below are the project details: Angular CLI: 8.2.0 Node: 14.19.1 OS: darwin x64 Angular: 8.2.0 ... animations, cli, common, compiler, compiler-cli, core, forms ... platf ...

Pass on only the necessary attributes to the component

I have a simple component that I want to include most, if not all, of the default HTML element props. My idea was to possibly extend React.HTMLAttributes<HTMLElement> and then spread them in the component's attributes. However, the props' ...

By specifying the union type being used, the TypeScript compiler is informed

Imagine I have the following type: type TMyType = { a: string; b: number; c: number; d?: SpecialTypeA | SpecialTypeB | SpecialTypeC; } How can I specify in typescript that I am aware of the type of d in my (React) child components? I am hoping f ...

Guide on creating a server-side middleware in Next.js

In my upcoming nextjs project, which is a monorepo with both frontend and backend components, I am in need of a middleware to intercept requests. This middleware will handle tasks such as authentication and other necessary controls for every API call. Alth ...

Discover the steps to activate and utilize mat-error without the need for form control manipulation

Have you encountered an issue with using ngModel and mat-error without a form? Is there a workaround that allows the use of mat-error with ngModel? #code <mat-form-field appearance="fill" class="w-48per"> <mat-label>Fi ...

Adding properties to a class object in Javascript that are integral to the class

Recently, I've been contemplating the feasibility of achieving a certain task in JavaScript. Given my limited experience in the realm of JavaScript, I appreciate your patience as I navigate through this. To illustrate what I am aiming for, here' ...

Creating a personalized theme for Material UI 5.0 using Typescript with React

Having some trouble customizing a theme in Material UI 5.0 with typescript. theme.ts import { createTheme } from '@mui/material'; declare module '@mui/material/styles' { interface Theme { custom: { buttonWi ...

Retrieve the variable only once a response has been received from the POST request

Is there a way to update a variable in my component only after receiving a response from a POST request? Here is the code in component.ts: formSubmit() { this.sent = this.submitProvider.sendByPost(this.form); this.formSent = this.submitProvider.f ...

How come Typescript allows me to generate intersection types that seem impossible?

Despite being unimplementable, the type definition below does not trigger any warnings from the compiler. // No type error type impossible = 0 & string[] & 'anything' An item cannot simultaneously be a number, a string[], and a stri ...

What is the best way to convert a tuple containing key/value pairs into an object?

How can the function keyValueArrayToObject be rewritten in order to ensure that the type of keyValueObject is specifically {a: number; b: string}, instead of the current type which is {[k: string]: any}? const arrayOfKeyValue = [ {key: 'a', val ...

Guide on how to create a custom response using class-validator in NestJS

Is it feasible to customize the error response generated by class-validator in NestJs? The default error message structure in NestJS looks like this: { "statusCode": 400, "error": "Bad Request", "message": [ { "target": {} ...

How to update the page title in React TypeScript 16.8 without using Helmet

I have created a custom 404 not found page, and I would like the title of the page to change when someone navigates to it. Unfortunately, I do not want to use Helmet for this purpose, but I am struggling to make constructor or componentDidMount() work in ...

What steps should I take to successfully deploy my Vite-React and Typescript application on Heroku?

I'm having difficulty deploying my front-end Vite application to Heroku because it seems to be searching for a start script, which Vite doesn't have. Here are the logs I am seeing: 2022-08-23T23:34:35.000000+00:00 app[api]: Build succeeded 2022- ...

How can I access a file uploaded using dxFileUploader?

I am facing an issue with retrieving a file from dxFileUploader (DevExpress) and not being able to read it in the code behind. The file is only appearing as an object. Here is My FileUploader : { location: "before", ...

Application built with Electron and Typescript encounters module-related crash

While working on developing a client using Electron with Typescript, I encountered the following error: https://i.sstatic.net/7qLGh.png The configuration in tsconfig.json looks like this: { "compilerOptions": { "target": "e ...

Utilize JQuery to choose the angular element

Can Angular tags be selected using JQuery? I am currently utilizing the ui-select Angular component, which is integrated into the HTML page as shown below: <ui-select ng-model="rec.currencyCode" on-select="ctrl.selectCurrencyCode(rec, $item)"> & ...

saving the MediaObject to Ionic's storage with the set method

I attempted to save a Media Object on ionic storage, but encountered an error message stating: "error while saving media object in storage.set" https://i.sstatic.net/5jEaQ.jpg How can I successfully save a media object using storage.set and retrieve it ...

The @Input() function is failing to display or fetch the latest value that was passed

I am currently working on an angular project, and I've encountered a situation where I'm attempting to send a value from a parent component to a child component using the @Input() decorator. Despite my efforts, the child component continues to di ...