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 5 ngx-modialog issue TS2307: Module 'ngx-modialog/plugins/vex' not located

After installing module ngx-modialog using the Angular 5 CLI like this: npm install --save ngx-modialog I then added it to my app.module.ts: import { VexModalModule } from "ngx-modialog/plugins/vex"; import { ModalModule } from "ngx-modialog"; @NgModul ...

I am facing an issue with updating the mat-table after pushing values to a

I have a uniqueFormGroup with UniqueFormArray and a special-table that displays the array. When I add new uniqueFormGroup to UniqueFormArray, the special-table doesn't add new row. I was attempting to implement trackBy, but I am unsure of where (and ...

Creating a definition for the use of sweet alerts within a service and incorporating them through

Implementing sweet alert for displaying alert messages in angularJS2/typescript. Due to the repetitive nature of this code in different parts of the application, a service was created. @Injectable() export class AlertMessageService { constructor(pr ...

How can I generate codegen types using typeDefs?

I have been exploring the capabilities of graphql-codegen to automatically generate TypeScript types from my GraphQL type definitions. However, I encountered an issue where I received the following error message: Failed to load schema from code file " ...

The value of "metadata" is not a valid export entry for Next.js

After I installed Next.js 14 with TypeScript, I encountered an error related to my metadata type definition. import type { Metadata } from "next"; export const metadata: Metadata = { title: "next app", description: "next app 1 ...

What is the process for displaying the attributes of a custom object in Typescript?

I need help returning an array of prop: value pairs for a custom object using the myObject[stringProp] syntax. However, I keep encountering this error message: TS7053: Element implicitly has an 'any' type because expression of type 'str ...

What is the best way to automatically refresh an observable every 30 seconds?

@Component({ selector: 'app-geo', templateUrl: <img mat-card-image [src]="profileUrl | async "> export class GeoComponent implements OnInit { date; profileUrl: Observable<string>; constructor(private tempService ...

Leveraging the power of Framer Motion in combination with Typescript

While utilizing Framer Motion with TypeScript, I found myself pondering if there is a method to ensure that variants are typesafe for improved autocomplete and reduced mistakes. Additionally, I was exploring the custom prop for handling custom data and des ...

Leveraging Angular's capability to import files directly from the assets

I recently installed a library via npm and made some modifications to one of the modules. python.js If I delete the node_modules folder and run npm install, I am concerned that I will lose my changes. Is there a way to preserve these modifications by mov ...

Why does TypeScript trigger an ESLint error when using `extend` on a template string?

I am looking to create a TrimStart type in the following way: type TrimStart<T extends string> = T extends ` ${infer Rest}` ? TrimStart<Rest> : T; type TT = TrimStart<' Vue React Angular'>; // 'Vue React Angular' H ...

In Typescript, you can easily group a string into sections that consist of digits like 345-67, along with text containing a

I have a string that looks like this: "[111-11] text here with digits 111, [222-22-22]; 333-33 text here" and I am trying to parse it so that I can extract the code [111-11], [222-22-22], [333-33] along with their respective text descriptions. The challeng ...

Ways to extract values from a javascript hash map by exclusively incorporating an array

So here's the issue I'm encountering. Let's consider the following scenario: let surfaces: Map<any, any> = new Map([{"83.1" => Object}, {"84.1" => Object}]) let arr1 = ["83.1"] This is the desired o ...

struggling with configuring dependency injection in NestJS and TypeORM

Struggling with integrating nestjs and typeorm for a simple CRUD application, specifically facing issues with dependency injection. Attempting to modularize the database setup code and import it. Encountering this error message: [ExceptionHandler] Nest ...

I'm encountering a Typescript error where I'm unable to assign a function to RefObject.current and it's indicating that the function is not callable

Does anyone know why assigning a function type to a ref.current type is causing me issues? useEffect(() => { savedHandler.current = handler; // ERROR HERE: }, [handler]); TS2741: Property 'current' is missing in type '(e: Chang ...

Position the center of an Angular Material icon in the center

Seeking help to perfectly center an Angular Material icon inside a rectangular shape. Take a look at the image provided for reference. The current positioning appears centered, but upon closer inspection, it seems slightly off-center. It appears that the ...

Utilize the URL path entered by a user to navigate through the page

I'm exploring Angular 6 to develop my app and I need a feature that can grab whatever the user is typing into the address bar on the website. For instance: If a user types in the domain "myproject.example/5/cool", the site should display "User 5 is ...

In TypeScript, this regular expression dialect does not permit the use of category shorthand

Recently, I attempted to implement a regular expression in TypeScript: I ran the following code: const pass = /^[\pL\pM\pN_-]+$/u.test(control.value) || !control.value; To my surprise, an error occurred: "Category shorthand not allowed in ...

Ways to utilize Subjects for sharing global information in Angular 6

I have been struggling to find an effective way to share data between two components that have the same parent in an Angular application. Currently, I am working with an Angular Material stepper where Step 1 contains one component and Step 2 contains anot ...

The error message for Angular FormControl's minimum length validation is not showing up in the errors

My goal is to access the minlength error, but when I check all the errors, it's not there. Below is my form control title: new FormControl("", [Validators.minLength(10), Validators.required]), I expect to see both the required and minlengt ...

I recently updated all my projects to Angular 14, but when I tried to install a package using `npm i`, it still

The challenge at hand I encountered an issue with my two Angular projects. The first project serves as a library utilized by the second project. I recently upgraded both projects to Angular 14 following this guide. However, after running an npm i on the ...