What is the best way to create a custom type guard for a type discriminator in TypeScript?

Suppose there are objects with a property called _type_ used to store runtime type information.

interface Foo {
  _type_: '<foo>';
  thing1: string;
}

interface Bar {
  _type_: '<bar>'
  thing2: number;
}

function helpme(input: Foo|Bar): string | number {
  if (input._type_ === '<foo>') {
    return input.thing1;
  }
  if (input._type_ === '<bar>') {
    return input.thing2;
  }
  return 'N/A';
}

All of this works well and good, but I want to simplify the code by abstracting the _type_ check into a function so that other parts of the code don't need to be aware of this property.

interface Foo {
  _type_: '<foo>';
  thing1: string;
}

interface Bar {
  _type_: '<bar>'
  thing2: number;
}

function typeIs(o: any, t: '<foo>' | '<bar>'): o is {_type_: t} {
  return o && typeof(o) === 'object' && o._type_ === t;
}

function helpme(input: Foo|Bar): string | number {
  if (typeIs(input, '<foo>')) {
    return input.thing1;
  }
  if (typeIs(input, '<bar>')) {
    return input.thing2;
  }
  return 'N/A';
}

However, running this code results in an error:

't' refers to a value, but is being used as a type here. Did you mean 'typeof t'?

This error makes sense. How can I correct the syntax to achieve my goal?

https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGIHt3IN4ChnID6YAngA4SEBcyA5ADwyYB8tA3PsmABagDmARhoBnMFH4cAvrlyhIsRCgBCcKDk7FylGgwBGq1px78ATDRABXALa7oUmTAsgEYYOhBctASWEAKdDRwICQANFw6jCy0yAA+dPT6UKwAlDRYwMI4mhTUXJLqBFAQYBZQHlgAZBWeFOgw-snIALwtdOi6AFYQLtFVyOgAdNmUza1g9riOzq7uyNwQADZkVhC+oGQWYDQY6DEqUKnIouIgfLHIljbQBcjAMMi+pBQ+ayAbYGEMTOgpjXgEhWKpQ8602A2MpwEHAI0gIdweTwgL1BH3iiV+N0BJTKtzeYIhfBM0OQsOQRWxHloADkAPQAQXYuGkQA

Answer №1

Are you attempting to create a custom typeguard that verifies a dynamic type? Have you considered an alternative approach like the following?

interface Foo {
  _type_: '<foo>';
  thing1: string;
}

interface Bar {
  _type_: '<bar>'
  thing2: number;
}

function isFoo(o: any, typeEncoding: string = '<foo>'): o is Foo {
  return o && typeof(o) === 'object' && o._type_ === typeEncoding;
}

function isBar(o: any, typeEncoding: string = '<bar>'): o is Bar {
  return o && typeof(o) === 'object' && o._type_ === typeEncoding;
}

function helpme(input: Foo|Bar): string | number {
  if (isFoo(input)) {
    return input.thing1;
  }
  if (isBar(input)) {
    return input.thing2;
  }
  return 'N/A';
}

If your goal is to maintain dynamism in your code, consider exploring solutions such as Dynamic type guard functions

Answer №2

After receiving advice from DKidwell, I was inspired to utilize overloads in order to tackle the issue at hand (a method that can be implemented through code generation):

function typeCheck(obj: any, type: '<foo>'): obj is Foo;
function typeCheck(obj: any, type: '<bar>'): obj is Bar;
function typeCheck(obj: any, type: string): boolean {
  return obj && typeof(obj) === 'object' && obj._type_ === type;
}

function processInput(input: Foo|Bar): string | number {
  if (typeCheck(input, '<foo>')) {
    return input.property1;
  }
  if (typeCheck(input, '<bar>')) {
    return input.property2;
  }
  return 'N/A';
}

Although effective, there may be more efficient solutions available.

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

Is there a way to seamlessly share TypeScript types between my Node.js/Express server and Vite-React frontend during deployment?

I'm currently tackling a project that involves a Node.js/Express backend and a Vite-React frontend. My goal is to efficiently share TypeScript types between the two. How should I configure my project and build process to achieve this seamless type sha ...

Tips for utilizing a personalized design with the sort-imports add-on in VS Code?

After recently installing the VS Code extension sort-imports, I decided to give a custom style called import-sort-style-module-alias a try. Following what seemed to be the installation instructions (via npm i import-sort-style-module-alias) and updating m ...

Learn how to implement icons within Textfield components using Material-UI and TypeScript in React

I have successfully created a form with validation using TypeScript Material UI and Formik. Now, I am looking to enhance the visual appeal by adding a material UI Icon within the textfield area. Below is a snippet of my code: import React from 'reac ...

Serverless-offline is unable to identify the GraphQL handler as a valid function

While attempting to transition my serverless nodejs graphql api to utilize typescript, I encountered an error in which serverless indicated that the graphql handler is not recognized as a function. The following error message was generated: Error: Server ...

Leverage one Injectable service within another Injectable service in Angular 5

I am facing difficulties in utilizing an Injectable service within another Injectable service in Angular 5. Below is my crudService.ts file: import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; ...

what kind of bespoke entity in TypeScript

Exploring typescript for the first time, I have constructed this object: const startingState = { name: { value: "", error: false }, quantity: { value: 0, error: false }, category: "Grocery& ...

Retrieve the radio button value without using a key when submitting a form in JSON

Looking to extract the value upon form submission in Angular, here is the code: In my Typescript: constructor(public navCtrl: NavController, public navParams: NavParams, public modalCtrl: ModalController, public formBuilder: FormBuilder, public alertCtrl ...

Tips for incorporating attributes into a customized Material-UI props component using TypeScript in React

I'm interested in using material-ui with react and typescript. I want to pass properties to the components, but I'm having trouble figuring out how to do it. Currently, I'm working with the react-typescript example from the material-UI repos ...

Bold Chutzpah: Delving into AngularJS Testing using Jasmine and TypeScript

Currently, I am utilizing Angular 1.4.9 in combination with Jasmine 2.2.0 and Chutzpah 4.2.0. My Angular code and unit tests are both written in TypeScript within Visual Studio 2015 Update 1. The issue I am facing mirrors that which was previously discuss ...

Angular2 and ReactiveX: Innovative Pagination Strategies

Currently, I am diving into the world of ReactiveX. To make things easier to understand, I have removed error checking, logging, and other unnecessary bits. One of my services returns a collection of objects in JSON format: getPanels() { return this. ...

create an endless loop to animate in angular2

Is there a way to add iterations, or something similar to the ccs3 animation-iteration-count in Angular 2 animations? I've looked but can't find anything related. How can we apply an infinite play to the animation? Where should we include that o ...

Styling of Bootstrap HTML element not appearing as expected

Recently, I've been trying out a new approach by embedding Bootstrap components in an iframe. However, despite loading the necessary stylesheet and scripts, the elements seem to be missing their styles. Can anyone shed some light on why this might be ...

Developing a specialized command-line application for currency conversion is my current project

Currently, I am working on developing a command-line application for currency exchange. I have created an interface to define the structure of an object array that will store the keys and values of currency names along with their current values in the inte ...

What is the method in TypeScript for defining a property in an interface based on the keys of another property that has an unknown structure?

I recently utilized a module that had the capability to perform a certain task function print(obj, key) { console.log(obj[key]) } print({'test': 'content'}, '/* vs code will show code recommendation when typing */') I am e ...

Creating an object key using a passed literal argument in TypeScript

Can the following scenario be achieved? Given an argument, how can we identify the object key and access it? Any potential solutions? async function checkKey(arg:'key1'|'key2'){ // fetchResult returns an object with either {key1:&apo ...

The Primeng Angular2 checkbox malfunctioning issue

My form setup in Angular2 CLI looks like this: Inside the component export class UsersAddComponent implements OnInit { ngOnInit() { this.userForm = this._formBuilder.group({ role: ['', [Validators.required]], others: this._for ...

Obtain a value that is not defined

Good day, I am encountering an issue with my data not accepting an undefined value. Below is the code snippet: interface IModalContatos { dados: IContatos; onSave(dados: IContatos): void; onClose(): void; } When passing this data to my modal, I rece ...

Ways to verify if an array contains elements from other arrays in Typescript

How can I verify if the winningConditions are present in chosenNumbers? The chosenNumbers array is of varying lengths and consists of a mix of numbers. For example, one of the winning conditions is [0, 3, 6], but there is also the number 2 included. How ...

Tips for dynamically implementing a pipe in Angular 5

In my Angular application, I have implemented a filter using a pipe to search for option values based on user input. This filter is applied at the field level within a dynamically generated form constructed using an ngFor loop and populated with data from ...

When employing `queryParams` in Angular routing, the URL will automatically replace `%` with `%25`

When trying to use queryParams for navigation in Angular routing, I encountered an issue. <a routerLink='/master' [queryParams]="{query:'%US',mode:'text'}"><li (click)="search()">Search</li></a> The d ...