Resolve a function dynamically by utilizing a mapping of records

I am looking to run a specific handler for an input based on the type property of the input. The setup is as follows:

type MyObjectType = "alpha" | "beta";

interface MyAlphaObject {
  value: number;
  type: "alpha";
}

interface MyBetaObject {
  value: string;
  type: "beta";
}

type MyObject = MyAlphaObject | MyBetaObject;

const getValueOfAlpha = ({ value }: MyAlphaObject) => value;

const getValueOfBeta = ({ value }: MyBetaObject) => value;

type MyObjectTypeOf<T extends MyObjectType> = Extract<MyObject, { type: T }>;

type MyObjectValueOf<T extends MyObjectType> = MyObjectTypeOf<T>["value"];

type HandlerFunc<T extends MyObjectType> = (value: MyObjectTypeOf<T>) => MyObjectValueOf<T>;

const handlers: { [T in MyObjectType]: HandlerFunc<T> } = {
  alpha: getValueOfAlpha,
  beta: getValueOfBeta,
};

This setup works well when the type is already known at compile time, like:

✅ This example will work

const alpha: MyAlphaObject = {
    type: "alpha",
    value: 25
}

const handler = handlers["alpha"]

console.log(alpha)

❌ However, it fails if the type is only determined at runtime, such as:

const choices = ["alpha", "beta"] as const

const alpha: MyAlphaObject = {
    type: "alpha",
    value: 25
}

const beta: MyBetaObject = {
    type: "beta",
    value: "text value"
}

const randomChoice = choices[Math.round(Math.random() * 10)%2]

const handler = handlers[randomChoice]

console.log(handler(alpha))

Link to code playground: https://tsplay.dev/wOQgRm

Question:

  • Is there a way to make this work?
  • Are there more efficient alternatives?

Edit 2024-10-29 15:34 EST

In addition, achieving this is possible using:

  • long chains of ternaries
  • if-else statements
  • switch cases
  • forced assertions

For instance, the following approach is viable:

const result = randomChoice === "alpha" ? handlers[randomChoice](alpha) : randomChoice === "beta" ? handlers[randomChoice](beta) : null

console.log(result)

I am exploring options that can evade these methods if feasible.

Answer №1

handler plays a crucial role as it can be either the alpha handler or the beta handler. The uncertainty at compile time makes it impossible to unconditionally pass alpha to it. Determining which one you have and passing the suitable value is essential.

if (handler === handlers.alpha) console.log(handler(alpha))
else if (handler === handlers.beta) console.log(handler(beta))

If you wish for a handler function that caters to both, consider something like this:

const objMap = { alpha, beta } as const;
const randomType = choices[(Math.random() * 10) % 2]
const randomObj = objMap[randomType];

const handler: HandlerFunc<MyObjectType> = (obj: MyObject) => {
  switch (obj.type) {
    case 'alpha': return getValueOfAlpha(obj);
    case 'beta': return getValueOfBeta(obj);
  }
}

console.log(handler(randomObj))

To refine your mapping, utilizing a switch statement to narrow down the type would be advisable. Alternatively, casting safely at a certain point may be an option, but incorporating a switch statement is preferred if feasible.

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

Receiving 'Module not found' error in Typings on specific machines within the same project. Any suggestions on how to troubleshoot this issue?

I have a project cloned on two separate machines, each running VS2015 with Typings 1.8.6 installed. One machine is running the Enterprise version while the other has the Professional version, although I don't think that should make a difference. Inte ...

Include "+5" in cases where additional elements cannot be accommodated

I am working on a project where I have a div called ItemsContainer that dynamically renders an array of items (Item) depending on the screen size. While mapping through the array, I want to check if there is enough space to display the current item. If no ...

Error: Typescript error at line 18 in app.ts - Cannot locate the 'server' namespace

Check out this straightforward code snippet: "use strict"; import * as express from "express"; class Server { public app: express.Application; public static start(): Server { return new Server(); } constructor() { this. ...

Incorporate a typescript library into your Angular application

Recently, I added a text editor called Jodit to my angular application and faced some challenges in integrating it smoothly. The steps I followed were: npm install --save jodit Inserted "node_modules/jodit/build/jodit.min.js" in angular.json's bui ...

Using a component again with variations in the input data

I'm looking to reuse a card component to display different types of data, but the @Input() properties are for different objects (articles and events). Parent HTML: <card-component [headline]="Articles" [content]="art ...

Obtain the title of the function currently running in Angular 2 with TypeScript

Seeking assistance for an Angular 2 application employing TypeScript. In order to enhance my logging process, I am looking to dynamically retrieve and log the name of the class or component along with the function that triggered the logging action. Curre ...

What is preventing the spread type from being applied to `Record` in TypeScript?

export type AddResourceProps<K extends string, T extends any> = (resource: BasicResource) => Record<K, T> const addtionalResourse = addResourceProps ? addResourceProps(resource) : {} as Record<K,T> const result = { ...addtionalRe ...

Issue with Ant Design form validation

After reading through the documentation, I attempted to implement the code provided: Here is a basic example: import { Button, Form, Input } from "antd"; export default function App() { const [form] = Form.useForm(); return ( <Form f ...

Ways to incorporate horizontal scrolling in mat autocomplete

I have an Angular app with auto complete functionality. I am trying to add horizontal scroll to the mat-option by applying CSS styles, but it's not working as expected. .cdk-overlay-pane { overflow-x: auto; } I also tried following the instruc ...

Neglecting the error message for type assignment in the Typescript compiler

Presented here is a scenario I am facing: const customer = new Customer(); let customerViewModel = new CustomerLayoutViewModel(); customerViewModel = customer; Despite both Customer and CustomerLayoutViewModel being identical at the moment, there is no ...

Trouble with Typescript in VSCode made easy

Setting up a VSCode environment for working with TypeScript v2.03 has been challenging. Beginning with a simple vanilla javascript snippet that can be tested in node via the integrated terminal. function Person() { this.name = ""; } Person.prototy ...

What is the process for performing type checking on an array variable designated as "as const"?

Check out this code snippet: export type Types = 'a' | 'b'; export type MyPartials = { readonly [P in keyof Types]?: number; }; export interface MyI { readonly name: string; readonly myPartials: MyPartials; } export const myI ...

TS: How can we determine the type of the returned object based on the argument property?

Assume we have the following data types type ALL = 'AA' | 'BB' | 'CC'; type AA = { a: number; }; type BB = { b: string; }; type CC = { c: boolean; }; type MyArg = { type: ALL }; I attempted to create a mapping between type n ...

Angular/NestJS user roles and authentication through JWT tokens

I am encountering difficulties in retrieving the user's role from the JWT token. It seems to be functioning properly for the ID but not for the role. Here is my guard: if (this.jwtService.isTokenExpired() || !this.authService.isAuthenticated()) { ...

When a const variable is declared within the composition-api setup(), it remains unchanged unless redeclared within the function scope

Being primarily a back-end developer, the front-end side of things is still relatively new to me. I'm aware that there are concepts in this domain that I haven't fully grasped yet, and despite my efforts, I'm unable to resolve a particular i ...

The functionality of the TURF booleanwithin feature is malfunctioning and not producing the

Currently, I am working on validating whether a polygon is completely within another polygon. However, there are cases where more complex polygons should return false, but turf interprets them as valid. If you'd like to see the sandbox, click here: ...

Several cucumber reports are showing an error message containing special characters

I am encountering an issue in the multiple cucumber report where error messages are being displayed with special characters. Is there a way to format them properly? I am currently learning Playwright, Typescript, and CucumberJs and generating reports for m ...

Mastering unit testing with Behaviour Subjects in Angular

I am looking to test the get and set methods of my user.store.ts file. The get() method is used to retrieve users, while addUsers() is utilized to add new Users to the BehaviorSubject. How can I accomplish this? import { Injectable } from '@angular/c ...

Tips for importing modules in React JS/Next JS + Typescript as 'components/filename' rather than '../../../components/filename'

Is there a way to import pages, components, and containers directly using syntax like 'components/filename' or '@components/filename', rather than having to specify the full path to the parent folder such as '../../components/filen ...

Debugging TypeScript on a Linux environment

Approximately one year ago, there was a discussion regarding this. I am curious to know the current situation in terms of coding and debugging TypeScript on Linux. The Atom TypeScript plugin appears promising, but I have not come across any information ab ...