TypeScript uses the value of one of the parameters in a function to automatically infer its return type

I am working on a function where the return type needs to change based on one of its parameters. The parameter is called Command and has two predefined values: 'trueOrFalse' or 'logSomething'. The ultimate goal is to make this function adaptable so that more commands can be added in the future.

type Command = 'trueOrFalse' | 'logSomething';

The function in question is

handleCommand(command: Command) => ???
. I want this function to automatically determine the return type based on the command provided. For instance, when you call handleCommand('trueOrFalse'), it should know that the return type will be boolean.

Below is what I currently have:

type Command = 'trueOrFalse' | 'logSomething'

type CommandHandler = {
    'trueOrFalse': () => boolean;
    'logSomething': () => void;
}

type Handlers = {
    [Key in Command]: () => ReturnType<CommandHandler[K]>;
}

handleCommand<T extends Command>(command: T): ReturnType<CommandHandler[T]> {
    const handlers: Handlers = {
        'trueOrFalse': () => true,
        'logSomething': () => { console.log('Test') }
    }

    const handler = handlers[command] as () => ReturnType<CommandHandler[T]>;

    if (!handler) {
        //error
    }

    return handler();
}

Even though my function seems error-free now after making several adjustments for typing issues like defining the Handlers type and casting the handler constant, the actual problem remains unresolved. When calling the function with either 'trueOrFalse' or 'logSomething', the return type still ends up being boolean | void. How do I get it to recognize that when 'trueOrFalse' is used, the return type should strictly be a boolean?

Edit:

It seems the root of the issue lies in an intermediary function that invokes handleEvent.

This is the intermediary function:

triggerCommand(command: Command) {
    return this.commandDispatcher.handleCommand(command);
}

Here's where the type error occurs:

let bool: boolean;
bool = triggerCommand('trueOrFalse'); // compiling error - cannot assign boolean | void to boolean

To fix this, I had to explicitly define the return type of triggerCommand:

triggerCommand<T extends Command>(command: Command): ReturnType<CommandHandler[T]>

I apologize for not providing this context earlier as I overlooked the significance of this seemingly trivial intermediary function. It makes sense now why the expected behavior was not achieved.

Answer №1

It is important to ensure that triggerCommand is made generic as well.

function triggerCommand<T extends Command>(command: T) {
    return handleCommand(command);
}

let bool: boolean;
bool = triggerCommand('trueOrFalse'); // works perfectly

This approach allows the return type to be determined implicitly based on T, rather than being all-encompassing.

Check out the playground example

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

Error: Failed to execute close function in inappbrowser for Ionic application

Working on integrating the "in-app-browser" plugin with my Ionic project. Check out the code snippet below: const browser = this.iab.create(mylink, '_blank'); browser.on('loadstop').subscribe( data => { if ...

What causes a standard React component with a default render prop to not pass PropTypes validation successfully?

I'm currently working on a React component with a render-prop that has a generic type. To improve usability, I want to set a default value for the render-prop. The code is functioning correctly, but during type-checking, I encountered a warning regard ...

Error encountered during Typescript compilation: Type 'void' cannot be assigned to type 'Item[]'

Below are my typescript functions. When I edit in vscode, the second function does not show any error message. However, upon compilation, an error is displayed for the second function: error TS2322: Type 'Promise<void>' is not assignable t ...

Utilize ngx-translate in Ionic 2 for translating menu items

I have successfully implemented ngx-translate for multi-language support in my application. However, I am now looking to extend this functionality to my menu items. How can I achieve this for my 3 menu items with different titles? ts file appPages: Pag ...

Strategies for passing multiple props to a function in React using TypeScript, such as useState([]) and useState(boolean)

Dealing with API Structure { "default": [ { "id": 1, "name": "A" }, { "id": 2, "name": "B" }, { "id": 3, "name" ...

In what way does TypeScript assign an object's type based on its current value?

let object = { key1: 123, key2: 'abc' } If we analyze, the data type of object is: { key1: number, key2: string } However, there are times when I wish its datatype could be defined as: { key1: 123, key2: 'abc' } Moreover, consideri ...

Initializing various objects on the same interface type array in one line

Is there a way to inline initialize an array of the interface type IFooFace in TypeScript with different specific implementations, similar to how it can be done in C#? Or do I have to initialize my objects before the array and then pass them in? In C#, th ...

Error: Unable to access the 'myDate' property as it is not defined

I've been working on a simple code, but I keep encountering a browser error. The expressjs logs also show an error message. TypeError: Cannot read property 'myDate' of undefined at getReportTable (XXX\dist\controllers&bsol ...

How can I properly containerize an Express Gatsby application with Docker?

SOLUTION: I am currently working on a project involving an express-gatsby app that needs to be built and deployed using GitHub Actions. To deploy it on Heroku, I have learned that containerizing the app is necessary. As a result, I have created a Dockerfil ...

TypedScript: A comprehensive guide to safely omitting deep object paths

Hi there, I have a complex question that I would like some help with. I've created a recursive Omit type in TypeScript. It takes a type T and a tuple of strings (referred to as a 'path'), then removes the last item on the path and returns t ...

Tips for setting an argument with a promise data type

I am currently working on writing unit tests using jest to test two functions in a separate file called generator, where I generate fake data : generator.ts export async function generateReportData(overide = {}) { return { clientId: faker.data ...

What is the best way to retrieve children generated by a custom HTML component using idiomatic React methods?

I'm currently working on developing a search bar with predictive text input using a custom HTML component. The component generates multiple plain HTML children that I need to manipulate in order to achieve the desired functionality. Specifically, I ha ...

How can you define a type in Typescript that encompasses all types that are derived from a shared type?

TLDR: How can I declare a type in Typescript that covers all types extending a specific interface? Issue Description I am working on a custom React hook that handles logic for determining if an element is being hovered over. The design is loosely based o ...

The object contains an incorrect property that shouldn't be there

I am currently working on an application using NestJS with the Fastify adapter But I am encountering something strange with object construction. Going through all the related classes and methods: Controller endpoint handler @Get() @ApiOperation ...

Strange occurrences with HTML image tags

I am facing an issue with my React project where I am using icons inside img tags. The icons appear too big, so I tried adjusting their width, but this is affecting the width of other elements as well. Here are some screenshots to illustrate: The icon wit ...

Different ways to handle dialogs in React with TypeScript

I'm currently working on developing a modal component in React TypeScript and I'm facing some issues in determining the correct type for a reference of an HTML dialog element. import { useRef } from 'react' const MyModal: React.FC = () ...

What is the best way to integrate model classes within an Angular module?

I have a few classes that I want to keep as plain bean/DTO classes. They are not meant to be display @component classes, @Pipe classes, or @Directive classes (at least, that's what I believe!). I am trying to bundle them into a module so that they ca ...

Update the component to display the latest information from the Bryntum grid table

In the Vue component, I have integrated a Bryntum grid table along with a bar chart. Clicking on one of the bars in the chart should update the data displayed in the Bryntum grid table. However, I've encountered difficulty in reloading the entire Bryn ...

Each element in ngFor triggers the invocation of ngAfterContentInit by Angular

In my Angular 11 (Ionic 5) app, I have a scenario with two components: A ContainerComponent and a BoxComponent. Both of these components are completely transparent (template: '<ng-content></ng-content>'). The challenge is for the cont ...

Pushing information from an embedded array using Typescript

I am encountering an issue when trying to extract data from a nested array. The JSON object I have looks like this: { component: Assembly, title: "Assembly", path: "/assembly", sections: { title: "Frame Assembly", steps: { ["Step ...