Specifying the return type of a function as a combination of the types of the input arguments

Is there a way to safely implement the given function in TypeScript without using unsafe casts or an extensive number of function overloads with various input permutations?

interface Wrapped<T> {
    type: string;
    data: T;
}

interface WrappedA<T> extends Wrapped<T> {
    type: "a";
}

interface WrappedB<T> extends Wrapped<T> {
    type: "b";
}

type FuncProps<A = never, B = never> = { a?: A[]; b?: B[] };
const func = <A = never, B = never>({ a, b }: FuncProps<A, B>) => {
    const ret: Array<([A] extends [never] ? never : WrappedA<A>) | ([B] extends [never] ? never : WrappedB<B>)> = [];
    if (a != null) {
        const push: Array<WrappedA<A>> = a.map(value => ({ type: "a" as const, data: value }));
        ret.push(...push); // Error: Type 'WrappedA<A>' is not assignable to type '[A] extends [never] ? never : WrappedA<A>'.
    }
    if (b != null) {
        const push: Array<WrappedB<B>> = b.map(value => ({ type: "b" as const, data: value }));
        ret.push(...push); // Error: Type 'WrappedB<B>' is not assignable to type '[B] extends [never] ? never : WrappedB<B>'.
    }
    return ret;
};

// The Intended Result
const ret1 = func({ a: [1] }); // type: WrappedA<number>[]
const ret2 = func({ b: ["1"] }); // type: WrappedB<string>[]
const ret3 = func({ a: [1], b: ["1"] }); // type: (WrappedA<number> | WrappedB<string>)[]

I attempted using never to exclude certain types from the union type of the returned array but encountered difficulties.

Answer №1

Implementing a more specific return type in a function overload achieved the intended outcome:

interface Wrapped<T> {
    type: string;
    data: T;
}

interface WrappedA<T> extends Wrapped<T> {
    type: "a";
}

interface WrappedB<T> extends Wrapped<T> {
    type: "b";
}

type FuncProps<A = never, B = never> = { a?: A[]; b?: B[] };

interface Overload {
    <A = never, B = never>(props: FuncProps<A, B>): Array<([A] extends [never] ? never : WrappedA<A>) | ([B] extends [never] ? never : WrappedB<B>)>;
    <A = never, B = never>(props: FuncProps<A, B>): Array<WrappedA<A> | WrappedB<B>>;
}

const func: Overload = <A, B>({ a, b }: FuncProps<A, B>) => {
    const ret: Array<WrappedA<A> | WrappedB<B>> = [];
    if (a != null) {
        const push: Array<WrappedA<A>> = a.map(value => ({ type: "a" as const, data: value }));
        ret.push(...push);
    }
    if (b != null) {
        const push: Array<WrappedB<B>> = b.map(value => ({ type: "b" as const, data: value }));
        ret.push(...push);
    }
    return ret;
};

// Intended outcome
const ret1 = func({ a: [1] }); // type: WrappedA<number>[]
const ret2 = func({ b: ["1"] }); // type: WrappedB<string>[]
const ret3 = func({ a: [1], b: ["1"] }); // type: (WrappedA<number> | WrappedB<string>)[]

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

Angular-4: Exploring Component Reference on Click Event

One of my challenges involves dynamically adding different components when the user clicks, allowing them to add and remove any component. However, I am struggling to figure out how to reference the component where the user clicked in Angular-4. here are s ...

Having trouble resolving modules after generating tsconfig.json?

I recently added a tsx component to my next.js 13 project following the documentation. After creating the required tsconfig.json file, I encountered module not found errors when running npm run dev: $ npm run dev > [email protected] dev > n ...

Troubleshooting compilation issues when using RxJS with TypeScript

Having trouble resolving tsc errors in the code snippet below. This code is using rxjs 5.0.3 with tsc 2.1.5 import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; import 'rxjs/Rx'; let subject ...

What is the best way to establish the primary color for the entire app?

Is there a way to easily set the color for @react-native-material/core's theme? I managed to change the color but I don't want to have to do it individually for each component. ...

The pagination feature in ag-grid is malfunctioning, causing it to initially send a request to

Upon clicking the search button, a server call will be made to retrieve results and display them in the ag grid. The server will only return the specified number of records based on the pagination details provided with each click. Despite implementing the ...

Error: Model function not defined as a constructor in TypeScript, mongoose, and express

Can anyone help me with this error message "TypeError: PartyModel is not a constructor"? I've tried some solutions, but now I'm getting another error as well. After using const { ... } = require("./model/..."), I'm seeing "TypeError: C ...

Is there a way to access URL parameters in the back-end using Node.js?

How can I extract querystring parameters email, job, and source from the following URL? I want to use these parameters in my service class: @Injectable() export class TesteService{ constructor(){} async fetchDataFromUrl(urlSite: URL){ ...

Attempting to access a specific JSON key using Observables

Apologies for the question, but I'm relatively new to Typescript and Ionic, and I find myself a bit lost on how to proceed. I have a JSON file containing 150 entries that follow a quite simple interface declaration: export interface ReverseWords { id ...

Navigating Routes with Router in Angular 7: A Step-by-Step Guide

Within my sidebar navigation component, the sidebar.component.html file is structured as follows: <nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top" id="sideNav"> <a class="navbar-brand" href="#page-top"> <span cl ...

Combine all TypeScript enums into a single one

Looking for a way to combine two separate enums into one for easier use. export enum ActionTypes1 { ClearError = 'CLEAR_ERROR', PrependError = 'PREPEND_ERROR', } export enum ActionTypes2 { IncrementCounter = 'INCREMENT_COUNT ...

What is the best way to showcase a view on the same page after clicking on a link/button in Angular?

Is there a way to show a view on the same page in an Angular application when a link is clicked? Rather than opening a new page, I want it displayed alongside the list component. How can this be accomplished? Here's an illustration of my goal: I&apos ...

Utilizing the FormsModule and ReactiveFormsModule within a Component module

I am facing an issue with integrating a reactive form into a generated component called boom-covers. I am utilizing the [formGroup] property as shown below: <form name="boomCovers" method="post" id="bomCovers" (ngSubmit)=&q ...

Determining the quantity of variations within a union in Typescript

Is it possible to determine the number of types in a union type in Typescript, prior to runtime? Consider the following scenario: type unionOfThree = 'a' | 'b' | 'c'; const numberOfTypes = NumberOfTypes<unionOfThree>; c ...

Is it possible to include HTML elements like <href> in Vue data?

My collection of data strings looks something like this: data(){ return(){ {name:"example", title:"exampleTitle", desc:"exampleDescription exampleDescription ....."}, {name:"example2", title:"example2Title", desc:"exampleDescripti ...

What is the best way to declare a TypeScript type with a repetitive structure?

My data type is structured in the following format: type Location=`${number},${number};${number},${number};...` I am wondering if there is a utility type similar to Repeat<T> that can simplify this for me. For example, could I achieve the same resul ...

Angular: Implementing conditional HTTP requests within a loop

Currently, I am facing a challenge where I need to loop through an array of objects, check a specific property of each object, and if it meets certain criteria, make an HTTP request to fetch additional data for that object. The code snippet below represen ...

Encountering an error with Dynamic Control generic react-hook-form: Error code TS2322 appears - Type 'Control<FormFields, any>' cannot be assigned to type 'Control<FieldValues, any>'

Within my application, I am utilizing react-hook-form in conjunction with the latest version of MUI 5.11. I have developed a reusable Select component: ...someImports import { Control, Controller } from 'react-hook-form'; interface SelectProps { ...

The error message "The type 'MouseEvent' is non-generic in TypeScript" popped up on the screen

Having created a custom button component, I encountered an issue when trying to handle the onClick event from outside the component. I specified the parameter type for the onClickCallback as MouseEvent<HTMLButtonElement, MouseEvent>, which is typical ...

The deno bundle operation failed due to the absence of the 'getIterator' property on the type 'ReadableStream<R>'

When attempting to run deno with bundle, an error is encountered: error: TS2339 [ERROR]: Property 'getIterator' does not exist on type 'ReadableStream<R>'. return res.readable.getIterator(); ~~~~~~~~~~~ ...

Tips for inserting an HTML element within an exported constant

I need help formatting an email hyperlink within a big block of text. Here is the code snippet: const myEmail = '<a href="mailto:<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2e4b564f435e424b6e4b564f435e424b004d41 ...