Identify the signature of a callback function during runtime

My goal is to pass different callback functions as arguments and have them called with the proper parameters.

Here is a simplified example of how it should work. However, determining if process instanceof ITwo makes no sense and I haven't found an expression that accomplishes this.

    interface IOne { (j: string): number; }
    interface ITwo { (j: number): number; }
    function dualArg(i: string, process: IOne | ITwo): number {
        // something along these lines
        if (process instanceof ITwo) {
            // call with a numeric argument
            return process(i);
        } else {
            // convert to a number
            return process(+i);
        }
    }
    function inc(i: number): number {
        return ++i;
    }
    function idem(text: string): number {
        return +text;
    }
    it('determine function signature', () => {
        expect(dualArg('1', inc)).toBe(2);
        expect(dualArg('1', idem)).toBe(1);
    })

For a normal argument, using instanceof will suffice for TypeScript to recognize it as an object of a specific type. Unfortunately, there isn't a similar solution for functions.

If I resort to hard-coded conditionals like

process.prototype.constructor.name === 'idem'
, TypeScript throws an error message:
Cannot invoke an expression whose type lacks a call signature. Type 'IOne | ITwo' has no compatible call signatures.

In such cases, I could define process: any to bypass TypeScript checks and make the code compile and run. However, my intention is to be able to distinguish functions solely based on their signature, without relying on names or additional flags.

Answer №1

One challenge arises from the loss of type information at runtime. This means that it becomes difficult to determine the type of a function directly during runtime, except for recognizing it as a function.

A way around this limitation is to create a function type that includes a property indicating its type. By constructing the function using another function, you can overcome this hurdle:

enum Type { One, Two}
interface IOne { (j: string): number; type: Type.One }
interface ITwo { (j: number): number; type: Type.Two}
function dualArg(i: string, process: IOne | ITwo): number {
    if (process.type === Type.One) {
        // Type guard above, process is of type IOne
        return process(i);
    } else {
        // Type guard above, process is of type ITwo
        return process(+i);
    }
}
function inc(i: number): number {
    return ++i;
}
function idem(text: string): number {
    return +text;
}

function oneFunction(fn: (j: string)=> number) : IOne{
    return Object.assign(fn, { type: Type.One as Type.One });
}

function twoFunction(fn: (j: number)=> number) : ITwo{
    return Object.assign(fn, { type: Type.Two as Type.Two });
}

dualArg("", twoFunction(inc));
dualArg("", oneFunction(idem));

In a simplistic scenario like the one presented here, this solution may seem excessive (you could just define two versions of dualArg). However, in cases where the creation and usage of the function are disjointed and there is significant code reuse between them, this approach can prove valuable.

Answer №2

When it comes to TypeScript, it primarily operates during development, not at runtime. Expecting it to perform tasks in real-time is not its purpose. TypeScript has the ability to interpret things based on your assumptions at runtime, such as using instanceof.

It seems like consolidating your two functions into one implementation with multiple overloads would be more efficient, rather than handling it within the dualArg function. Addressing parameter types directly in dualArg would require duplicating the process whenever calling these functions.

Consider creating a wrapper function that conducts parameter testing (at runtime), allowing TypeScript to acknowledge and safeguard against potential errors.

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 with Firebase: How to ignore a field in a query

I am curious to see if my current structure is compatible with Firebase, or if I need to make adjustments. Let's take a look at an example using the "/rooms" endpoint, which contains an array of Room objects: export class Room { id: number; p ...

The TypeScript in the React-Native app is lacking certain properties compared to the expected type

I recently integrated the https://github.com/react-native-community/react-native-modal library into my project and now I need to create a wrapper Modal class. Initially, I set up an Interface that extends multiple interfaces from both react-native and reac ...

What are the best ways to utilize @types/bootbox and @types/jquery?

Is there a way to incorporate @types/bootbox and @types/jquery into an Angular 4 project? I attempted the following: npm install @types/bootbox and in my code, I am implementing it like so: import * as bootbox from 'bootbox'. However, I encou ...

When using Protractor with Typescript, you may encounter the error message "Failed: Cannot read property 'sendKeys' of undefined"

Having trouble creating Protractor JS spec files using TypeScript? Running into an error with the converted spec files? Error Message: Failed - calculator_1.calculator.prototype.getResult is not a function Check out the TypeScript files below: calculato ...

Utilizing NgModelGroup nesting in various components for improved data management

Whenever I attempt to nest NgModelGroup inside another NgModelGroup, Angular seems to just ignore it. I'm currently utilizing Angular 12 and Template-driven Forms. Here is how my code appears: app.component.html <form #form="ngForm"> ...

Declaration files for Typescript ESLint configurations

I've been researching this issue online, but I haven't been able to find any solutions. It could be because I'm not entirely sure what's causing the problem. What I'm trying to do is set a global value on the Node.js global object ...

Setting checkbox values using patchValue in Angular programming

I'm facing an issue with reusing my create-form to edit the form values. The checkbox works fine when creating a form, but when I try to edit the form, the values don't get updated on the checkbox. Below is the code snippet that I have been worki ...

Tips for Effectively Declaring a Variable with React's useState

How should I correctly specify variable types in useState? In the code below, the value for alert must be either "success","warning", "error", or "info" const [alertValue, setAlertValue] = useState("error" ...

Exploring the Behavior of Typescript Modules

When working with the module foo, calling bar.factoryMethod('Blue') will result in an instance of WidgetBlue. module foo { export class bar { factoryMethod(classname: string): WidgetBase { return new foo["Widget" + classname](); ...

Problem with Grouping of Columns in Material-UI

Having some trouble with column grouping in MUI data grid pro. I am using typescript and trying to implement column grouping, but encountering issues with the module GridColumnGroupingModel, which is used as the type definition for columnGroupingModel. Fol ...

tsc will automatically incorporate additional files

I'm grappling with a frustrating issue related to tsc that's really getting to me. The problem involves having a file b.ts in the src folder, and another file a.ts in the project root folder. Here is an excerpt of my tsconfig file: { "comp ...

Unable to export data from a TypeScript module in Visual Studio 2015 combined with Node.js

Within one file, I have the code snippet export class Foo{}. In another file: import {Foo} from "./module.ts"; var foo: Foo = new Foo(); However, when attempting to run this, I encountered the following error: (function (exports, require, module, __file ...

Executing a Function in a Service from a Component in Angular/TypeScript and Receiving a Return Value

I need guidance on how to effectively utilize the "getUserDocInfo()" function from a separate service within my component. How can I call this function and then leverage the data it returns for further operations? Component Example getToken(){ this. ...

What is the best way to test a local variable in Angular 2 using karma and jasmine?

I've been working on writing a unit test with jasmine, but I've run into an issue when trying to test a local variable using jasmine. I have successfully tested a global variable in the past, but testing a local variable seems to be more challeng ...

Is there a way to validate user input in the front-end using my ANTLR grammar implemented in the back-end?

I have implemented a system using the ANTLR parser in Java for the backend of our software (frontend in Angular 2+). While the connection between the frontend inputs and backend logic is working well, there is a concern that users may input data with typos ...

Encountering an error in Angular 8 with the plugin: Unhandled ReferenceError for SystemJS

I recently followed a tutorial on integrating plugins into my Angular application at this link. I'm trying to create a component in my Angular app that can execute and display an external component. However, I encountered the following error: Uncaugh ...

Ways to identify modifications from a BehaviorSubject and automatically trigger a response according to the updated value

I am currently implementing a BehaviorSubject for managing languages in my Angular project. I am also utilizing Angular Datatables and trying to dynamically change the language displayed in the data table based on the value returned from the subscription. ...

Exploring the power of Typescript and Map in Node.js applications

I am feeling a little perplexed about implementing Map in my nodejs project. In order to utilize Map, I need to change the compile target to ES6. However, doing so results in outputted js files that contain ES6 imports which causes issues with node. Is t ...

Avoid using propTypes for props verification

Looking for a solution to handle multiple props on a button: interface buttonProps { secondary?: boolean; tertiary?: boolean; width?: number; children?: any; icon?: string; } If the button includes an icon without any children, how can ...

Issue with updating boolean values in reactive form controls causing functionality to not work as expected

I am currently facing an issue with validating a group of checkboxes. The main problem is that even though the 'this.dataService.minRequired' variable updates in the service, the validation state does not reflect these changes. I initially though ...