Using TypeScript to conditionally type input arrays

My goal is to create a function that accepts an array of variables that can belong to a few different types.

For each specified type, the second argument (callback) of the function will receive an array of corresponding types.

The relationship is such that FooRequest corresponds to Foo, BarRequest corresponds to Bar, and so on.

TypeScript Playground Link

type FooRequest = {
    _tag: 'foo',
}

type BarRequest = {
    _tag: 'bar',
}

type DurRequest = {
    _tag: 'dur',
}

type AllRequests = FooRequest | BarRequest | DurRequest;

type Foo = { t: 'foo', age: number };
type Bar = { t: 'bar', name: string };
type Dur = { t: 'dur', date: Date };

// chained conditional type mapping each "request" type to it's counterpart "result" type
type ConditionalOutput<T extends AllRequests> = 
    T extends FooRequest ? Foo : 
    T extends BarRequest ? Bar : 
    T extends DurRequest ? Dur : never;

/**
 * I am exploring how to define the callback `result` parameter to align with
 * the counterpart types for each of the provided input types.
 * 
 * For instance
 * input: [{ _tag: 'foo' }, { _tag: 'bar' }]
 * result type: [Foo, Bar]
 */
function makeArrayRequests<T extends AllRequests>(input: T[], callback: (result: ConditionalOutput<T>[]) => void) {
    // additional logic here to trigger the callback can be implemented
}

makeArrayRequests([{ _tag: 'foo' }, { _tag: 'bar' }], ([a, b]) => {
    // My desired outcome is for a to automatically be recognized as Foo and b as Bar
    // However, currently both a and b are of type `Foo | Bar`
});

Answer №1

Shoutout to @CertainPerformance for guiding me towards the amazing mapped tuple features, which helped me achieve my goal. However, there's a small twist - I need to include as const in the input parameter.

Check out the TypeScript Playground Link

type FooRequest = {
    _tag: 'foo',
}

type BarRequest = {
    _tag: 'bar',
}

type DurRequest = {
    _tag: 'dur',
}

type AllRequests = FooRequest | BarRequest | DurRequest;

type Foo = { t: 'foo', age: number };
type Bar = { t: 'bar', name: string };
type Dur = { t: 'dur', date: Date };

// chained conditional type mapping each "request" type to it's counterpart
type ConditionalOutput<T extends any> = 
    T extends FooRequest ? Foo : 
    T extends BarRequest ? Bar : 
    T extends DurRequest ? Dur : never;

type TupleMapper<A extends readonly AllRequests[]> = {
    [Index in keyof A]: ConditionalOutput<A[Index]>;
};

function makeArrayRequests<T extends readonly AllRequests[]>(input: T, callback: (results: TupleMapper<T>) => void) {

}

// `as const` is required but it works
makeArrayRequests([{ _tag: 'foo' }, { _tag: 'bar' }] as const, (v) => {
    const[
        a, // Foo
        b, // Bar
    ] = v;
    console.log(a.age);
    console.log(b.name);
});

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

Change a nullable string property within an interface to a non-nullable string property

Looking at two interfaces, one with a nullable vin and the other without: interface IVehicle { vin: string | null; model: string; } interface IVehicleNonNullVin { vin: string; model: string; } The goal is to convert a model from IVehicle ...

What is the best way to consistently and frequently invoke a REST API in Angular 8 using RxJS?

I have developed a REST API that retrieves a list of values. My goal is to immediately invoke this API to fetch values and store them in a component's member variable. Subsequently, I plan to refresh the data every five minutes. Upon conducting some ...

Determine whether or not there are any duplicate elements within an array object

Is there a way to determine true or false if there are any duplicates within an object array? arr = [ { nr:10, name: 'aba' }, { nr:11, name: 'cba' }, { nr:10, name: 'aba' } ] arr2 = [ { year:2020, cit ...

Achieving Jest integration with Angular 9 in a Storybook setup

We are currently utilizing Storybook 5 alongside Angular 9, with Jest 26 for some of the testing procedures. The issue we're facing arises when using Typescript version below 3.8.0 - a requirement for Angular 9's ng build --prod. This results in ...

Avoiding data type conversion in JavaScript/TypeScript

Currently delving into the world of JavaScript, I come from a background of working with statically typed languages. Naturally, I opted to dive into TypeScript instead of starting directly with JS. While TypeScript is great and addresses many issues presen ...

Ensure that all files with the extension ".ts" take precedence and are imported

I am facing an issue with my component as I have two files associated with it: app/components/SomeButton.ts app/components/SomeButton.tsx The .ts file contains most of the logic and code, while the .tsx file extends the .ts and only contains the ren ...

Differences between JSX.Element, ReactNode, and ReactElement: When should each be utilized?

Currently in the process of transitioning a React application to TypeScript. Everything seems to be going smoothly, however I've encountered an issue with the return types of my render functions, specifically within my functional components. In the p ...

Combining keys from an array of objects into a single array categorized by key names

When working with an array of objects, I need to filter and extract all the keys. One challenge I face is when there are nested objects, I want to concatenate the key names to represent the nested structure. For example: const data = [ id: 5, name: "S ...

Can a type alias be created for more than one parameter of a class or function with multiple type parameters?

When using Vue, there are situations where a generic function may require 3, 4, or even 5 type parameters. Is it possible to create a type alias for these parameters in order to avoid typing them out repeatedly? Something like this perhaps: // Example of ...

How can I populate an array using values from a different array in Angular?

I am facing an issue with populating three other arrays based on the property 'game type' from my array called 'my games'. Here is an example of the structure of the 'my games' array: hideScore: true, started: true, startedAt: ...

Encountering an issue in the test file when using react-router-dom v6: "The 'history' property is not found on the 'IntrinsicAttributes & RouterProps' type."

Main script: import { useContext, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import AuthenticationContext from './AuthenticationContext'; function HandleOAuthCallbackRoute() { co ...

Utilizing a segment of one interface within another interface is the most effective method

In my current project using nextjs and typescript, I have defined two interfaces as shown below: export interface IAccordion { accordionItems: { id: string | number; title: string | React.ReactElement; content: string | React. ...

How to use ngModel directive in Angular to select/unselect dynamically generated checkboxes and retrieve their values

Currently, I am working with a dataset retrieved from an API and dynamically creating checkboxes in my HTML page using the DataView component from PrimeNG. My objective is to implement a feature where users can select or deselect all checkboxes with a cli ...

How can Multer library be effectively utilized to manage exceptions in NestJS controllers?

While working on creating a service to upload specific files from a Post multipart/form-data request, I came across an easy way to validate the fields count and name sent using the FileInterceptor decorator from @nestjs/platform-express. However, I'm ...

What steps can I take to make my animation work in the opposite direction as well?

I'm currently working with an angular slider that is set to TRUE/OPEN by default. The issue I am facing is that while I am able to slide it using angular animations in one direction, I am unable to see the transition when sliding it back. Any assistan ...

What is the best way to display just one record that has the lowest score out of all the records?

I need help with displaying only 1 record from the DL list that has the lowest score, instead of showing all records. In the example on stackblitz, you can see that for the first record, the DL scores are: 54, 20, and updated. Instead of displaying all 3 ...

What is causing the Typescript compiler to interpret an element in a string array as the type 'never'?

My Typescript function compiled without issue in version 3.5.3, but after updating to 3.8.3, it now throws a confusing error during compilation. import { isNumber, toInteger, padNumber } from './math'; parse(value: string): NgbDateStruct { if ...

What is the best way to incorporate Tradingview's JavaScript into the render function of a React Typescript

I'm trying to incorporate some widgets into my Typescript React component. Here is the embed code export default class App extends React.Component { render(): ReactNode { return ( <div> Chart test <div className= ...

Angular 4 incorporates ES2017 features such as string.prototype.padStart to enhance functionality

I am currently working with Angular 4 and developing a string pipe to add zeros for padding. However, both Angular and VS Code are displaying errors stating that the prototype "padStart" does not exist. What steps can I take to enable this support in m ...

Encountering a problem with Vue StripeCheckout while navigating to a different component

I'm looking to integrate the StripeCheckout component into my Vue application. After copying and updating their example code using the composition API from here, everything works fine when I route to the subscribe component. However, if I try to navig ...