Typescript can represent both optional and required generic union types

Purpose

My goal is to establish an optional parameter unless a specific type is provided, in which case the parameter becomes mandatory.

Desired Outcome

I aim for the get method below to default to having an optional parameter. However, if a type TT is passed when calling the get method, it should then require a parameter of type TT.

export class Test<T = void> {

    get<TT>(param: T & TT): Test<T & TT>
    get<TT>(param: void | TT): Test<T & TT>
    get<TT>(param: T & TT): Test<T & TT> {
        return null as any;
    }

}

const test = new Test();

test.get();

test
    .get<{ name: string }>({ name: '' })
    .get() // Expected 1 arguments, but got 0.
    .get<{ age: number }>({ age: 23 }); // Property 'name' is missing in type '{ age: number; }' but required in type '{ name: string; }'

The above code will compile without errors, but my intention is for it to produce the specified error messages in the comments. Any suggestions on how I can achieve this?

Answer №1

UPDATE 20 September 2021

To achieve this, you need to engage in some TypeScript acrobatics.

Take a look at this instance.

Elucidation can be found in the comments

class Test<T = void> {

    /**
     * If T is void (is not provided)
     * we dont expect any arguments,
     * hence 
     * 
     * But if T is provided - expect T as an argument
     * and return Test<T> respectfuly
     */
    get<U extends {
        0: 'T is void',
        1: 'T is provided'
    }[T extends void ? 0 : 1]>(
        ...param: (
            U extends 'T is provided'
            ? [T]
            : []
        )
    ):
        U extends 'T is provided' ? Test<T> : Test
    /**
     * If T extends void - argument should
     * have U type
     * If T provided and it is pseudo equal U - expect T & U
     * If T provided and it is a subtype of U - expect T
     * If T is provided and it is not a subtype of U - expect T & U
     */
    get<U>(
        ...param: (T extends void
            ? [U]
            : T extends U
            ? (U extends T
                ? [T & U]
                : [T]
            )
            : [T & U]
        )
    ): Test<U>
    /**
     * Default types
     */
    get<U>(...param: Partial<U[]>) {
        return null as any
    }

}

const test = new Test(); // ok

test.get() // ok

const result = test
    .get<{ name: string }>({ name: '' }) // ok
    .get() // error Expected 1 arguments, but got 0.
    .get<{ age: number }>({ age: 23 }); // error

Playground

I made certain assumptions regarding the requirements. In case they do not align with your needs, please provide more information within your query.

At present, this response technically accomplishes your objective

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

How can TypeScript be forced to output a specific data type?

I've created a generic "fetcher" function that is designed to handle different types of entities. However, I'm encountering an issue where TypeScript is inferring the return type based on all possible conditions within the function. Is there a w ...

Cannot assign an array of Typescript type Never[] to an array of objects

Creating an object array can sometimes lead to errors, let's take a look at an example: objectExample[a].push({ id: john, detail: true }); objectExample[a].push({ id: james, detail: false}); const objectExample = { a = [ { id: john, detail: true} ...

What is the method for generating an observable that includes a time delay?

Question In order to conduct testing, I am developing Observable objects that simulate the observable typically returned by an actual http call using Http. This is how my observable is set up: dummyObservable = Observable.create(obs => { obs.next([ ...

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 ...

Guide on validating a dropdown using template-driven forms in Angular 7

Having trouble validating a dropdown select box, possibly due to a CSS issue. Any suggestions on how to fix this validation problem? Check out the demo here: https://stackblitz.com/edit/angular-7-template-driven-form-validation-qxecdm?file=app%2Fapp.compo ...

Display options based on the value selected in the preceding selection

How can I dynamically display select options in an Angular select based on a previously selected value? Take a look at the code snippet below. Here, I have implemented a conditional display of select options (Target 1/Target 2) based on the value selected ...

When fetching data from the API in Angular, the response is displayed as an object

After fetching data from the API, I am encountering an issue where the "jobTitle" value is not displaying in the table, but instead appears as an array in the console. Can someone assist me with resolving this problem? Below is the visibility.ts file: exp ...

Issue with playing audio file using HowlerJS

Having trouble playing a .mp3 file in my project directory with Howler. I'm not sure if there's an error in my src. When I tried playing an online hosted audio file, it worked fine. I've placed the audio file in the same directory as Slideon ...

Is there a way to apply the same technique to a dynamic select option in Angular?

My array is dynamic and has an onChange method in the select option. The issue arises when I add a new array and select the new option, as it causes the first array to reset. Here's a snippet of my array structure: <ng-container formGroupName=&qu ...

An issue with the Pipe searchByType is resulting in an error stating that the property 'type' is not found on the type 'unknown'

I keep encountering a range of errors like roperty does not exist on type 'unknown' after running the command ionic build --prod --release src/app/pages/top-media/top-media.page.ts:18:16 18 templateUrl: './top-media.page.html', ...

Implementing try-catch logic for element visibility in Playwright using TypeScript poses limitations

There is a specific scenario I need to automate where if the title of a page is "Sample title", then I must mark the test as successful. Otherwise, if that condition is not met, I have to interact with another element on the page and verify if the title ch ...

Issues with style not loading properly within innerHTML in Angular2

Currently, I am in the process of developing a page using Angular2/4 that includes a left navigation bar. To achieve reusability, I have separated this left menu into its own component and nested it within the main component. The objective is to utilize th ...

The model fails to update when a blur event occurs

I am a beginner with Angular2 and I am currently working on creating a reactive form, specifically an interactive date input field. Here is my HTML code: <div class="date ui-input"> <input type="text" name="dateD" [ngModel]="model.date | dat ...

What is the process for personalizing the appearance in cdk drag and drop mode?

I have created a small list of characters that are draggable using Cdk Drag Drop. Everything is working well so far! Now, I want to customize the style of the draggable items. I came across .cdk-drag-preview class for styling, which also includes box-shado ...

Encountering Typescript errors when trying to destructure a forEach loop from the output of

There are different types categorized based on mimetypes that I am currently working with. export type MimeType = 'image' | 'application' | 'text'; export type ApplicationMimeType = '.pdf' | '.zip'; expor ...

What are the steps to create custom Typescript RecursiveOmit and RecursivePick declarations for efficient cloning routines?

For some time now, I have been attempting to create a declaration for RecursiveOmit and RecursivePick in cloning methods such as JSON.parse(JSON.stringify(obj, ['myProperty'])) type RecursiveKey<T> = T extends object ? keyof T | RecursiveKe ...

Make sure to call the loader function in React Router only when there are path params present

I'm currently implementing the new React Router, utilizing loader functions to fetch data based on the loaded element. My goal is to have certain APIs called regardless of the route, with additional APIs triggered for specific routes. However, I&apos ...

I attempted to unsubscribe from an observable in Angular, but I encountered an error stating that the unsubscribe function does not exist

Here is the code snippet from a components.ts file in an Angular project. I encountered the following error during compilation: ERROR merge/merge.component.ts:75:12 - error TS2551: Property 'unsubscribe' does not exist on type 'Observable& ...

What is the best way to transfer information from one column to another column with Office Scripts?

I'm in the process of automation with Microsoft Excel using Office Scripts, and I've hit a roadblock when trying to transfer data from one column to another. https://i.sstatic.net/56ipi.png Specifically, I need to move the data from the Date co ...

Encountering a premature closure error, specifically the inability to set headers after they have already been sent to the client, when trying to stream video

I am in the process of constructing a video streaming web server with Nestjs. I have diligently followed the steps outlined in the Nest documentation. Unfortunately, I encountered some errors... MY file.controller.ts import { Controller ...