Verify in TypeScript whether a property of an object is a function with a specified signature

I am facing an issue with a function that retrieves a property from an object.

// Utils.ts
export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] {
    if (key in obj) { return obj[key as K]; }
    throw new Error(`Invalid object member "${key}"`);
}

My aim is to verify if the returned property is a function with a specific signature and then proceed to call this property with a provided parameter.

The function getProperty() is utilized for dynamically fetching one of the object's methods and invoking it. I attempted the following:

let property: this[keyof this] = utils.getProperty(this, name);
if (typeof property === 'function') ) { property(conf); }

However, I encountered an error stating "Cannot invoke an expression whose type lacks a call signature. Type 'any' has no compatible call signatures." I acknowledge that the property retrieved by getProperty() can technically be of any type, but how can I ensure that it specifically conforms to a function with the signature (conf: {}): void?

Answer №1

Unfortunately, there are no typeof type guards available for function types as per design decisions mentioned in microsoft/TypeScript#2072. While there are no built-in options, TypeScript does support user-defined type guards, allowing you to define functions that can narrow down the type of their arguments.


Once TypeScript code is compiled into JavaScript, the static type system gets erased. This means that at runtime, information about parameters like (conf: {}) => void isn't retained and cannot be directly accessed. Testing if a value is a function using

typeof x === "function"
only confirms it's a function without revealing details about its parameters or types. Alternative checks such as x.length === 1 have limitations due to factors like rest parameters and default parameters.

At runtime, any specifics about function parameter types are lost, especially if the functions originated from non-TypeScript sources. The ability to test functions by calling them with sample parameters exists but poses risks of unwanted side effects. The challenge lies in validating a function's correct type before utilizing it in operations.


If you're willing to accept these constraints, one approach is to consider a function valid if its length property equals 1. Alternatively, you may implement additional tests like setting a custom property named isOfRightType to validate specific functions. Creating a type guard based on your runtime tests provides a way to discern suitable functions:

function isFunctionOfRightType(x: any): x is (conf: {})=>void {
   return (typeof x === 'function') && (x.length === 1); // or customize as needed
}

// ... later

let property: this[keyof this] = utils.getProperty(this, name);
if (isFunctionOfRightType(property)) { property(conf); } // avoids errors

Try out the code on TypeScript Playground

Answer №2

There is a slight error in your function signature that needs to be addressed. The key parameter should actually be of type K. This change will help improve inference when using constants for parameters:

export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] {
    if (key in obj) { return obj[key as K]; }
    throw new Error(`Invalid object member "${key}"`);
}

let foo = {
    fn (conf: {}): void {}
}
let fn = getProperty(foo, "fn");
fn({}); // callable 

The issue arises when utilizing a key that is a string and isn't validated at compile time. In such cases, the compiler lacks efficiency in providing assistance. Since indexing by an arbitrary string leads to potential ambiguity, the compiler assumes the return type could be any valid field type within the target object. It's essential to understand that runtime validation of function parameter types becomes ineffective as they get erased. However, validating the count of parameters at runtime remains feasible:

let property:  Function = getProperty(this, name) as any;
if (typeof property === 'function' && property.length == 1)  
{ 
    property({}); 
}

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

Is there a way to verify an if-else statement in the ngStyle background property with Angular 7?

I have a collection of cards that need to be shown in a component. Each card contains a cover-image with an URL fetched from the server. In my component.html, I am using ngFor as follows: <div [style.background-image]="'url('+row.companyId?.c ...

When is the appropriate time to provide arguments to the constructor of a TypeScript service?

I am grappling with the concept of when to pass arguments to a service's constructor versus passing them in the execution of the service. For instance, I have a service that filters rows from an Excel sheet: @Injectable() export class FilterRowsServi ...

What is the process for type checking a Date in TypeScript?

The control flow based type analysis in TypeScript 3.4.5 does not seem to be satisfied by instanceof Date === true. Even after confirming that the value is a Date, TypeScript complains that the returned value is not a Date. async function testFunction(): ...

Are Named Capture Groups in Typescript Regex malfunctioning?

Update: I have included @babel/plugin-transform-named-capturing-groups-regex, following the suggestion from @Jack Misteli to explore Babel. Update:: Utilizing https://babeljs.io/en/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&buil ...

Determining the size of a custom-typed array in Typescript

Can anyone explain how to find the length of a custom typed array? For example: type TMyArray = IProduct[] interface IProduct { cost: number, name: string, weight: number } So, how can we determine the length in this case: const testArr: TMyArray ...

Deleting specialized object using useEffect hook

There's a simple vanilla JS component that should be triggered when an element is added to the DOM (componentDidMount) and destroyed when removed. Here's an example of such a component: class TestComponent { interval?: number; constructor() ...

Encountered a problem while trying to retrieve HTML values from an object within a ReactJS component

I have encountered an issue while working with an object that contains HTML values. When trying to access it, I am facing the following error: Element implicitly has an 'any' type because expression of type 'any' can't be used to ...

Display array elements in a PDF document using pdfmake

Upon reaching the final page of my Angular project, I have an array filled with data retrieved from a database. How can I utilize pdfmake to import this data into a PDF file? My goal is to display a table where the first column shows interv.code and the ...

Issue with MUI DialogTitle Ignoring SCSS Customization

I've been attempting to integrate a Google font into the text found in a Material-UI DialogTitle component. Here's my setup in App.module.scss: @import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@1,600&display=sw ...

Is there a technique to block small increments in a Time Input field?

Currently in the process of developing a tool to automate task scheduling within an Angular app. I am looking for a way to restrict the user's input when selecting the hour for task execution without having to install a complex input management packag ...

Ways to achieve the organization of nested promises in Angular 9

I am facing an issue with the order of asynchronous calls in my Angular script. Despite having multiple nested async calls, the order of execution seems to be disrupted. Here is a snippet of my Angular code: saveArticles(articles, action) { articles.f ...

Guide to automatically blur the input field and toggle it upon clicking the checkbox using Angular

<input (click)="check()" class="checkbox" type="checkbox"> <input [(ngModel)]="second_month" value="2019-09" type="month" [class.blurred]="isBlurred">> I need the second input(type=month) field to be initially blurred, and only unblur ...

Angular 2 iframe balked at being shown

Whenever I enter a URL into a text input and press enter, my objective is to have the URL open in an iframe. However, some websites produce an error when trying to do so. Is there a method to successfully display a web page in an iframe? Error message: Th ...

I need to search through a tree structure in typescript based on a specific value without encountering a maximum stack call exceeded error

How can I perform a value-based search on a complex tree structure in TypeScript without encountering the maximum stack call exceeded error? I am attempting to navigate through an expandable tree using TypeScript, and I will provide the code snippet below ...

Achieving the incorporation of multiple components within a parent component using Angular 6

Within parent.component.html The HTML code I have implemented is as follows: <button type="button" class="btn btn-secondary (click)="AddComponentAdd()">Address</button> <app-addresse *ngFor="let addres of collOfAdd" [add]="addres">< ...

What is the process for embedding a Google Chart within an Angular 4 application?

What is the process of integrating a Google chart in an Angular 4 application? After reading the answer to a question on Stack Overflow here, I realized that there were some missing details. Following a similar approach as mentioned in the previous answer ...

Establishing the placement of map markers in Angular

Currently, I am in the process of developing a simple web application. The main functionality involves retrieving latitude and longitude data from my MongoDB database and displaying markers on a map, which is functioning correctly. However, the issue I&apo ...

Alter the language settings of the Datepicker feature in Material Angular 4

Need help changing the language of Datepicker in Material Angular. Struggling to locate this information in the Angular material 2 documentation. Check out this plunkr https://plnkr.co/edit/unzlijtsHf3CPW4oL7bl?p=preview <md-input-container> < ...

Discover how to showcase the balances of various supported networks through the Wagmi Library in a React application, just like how it's executed in

When using wagmi to connect to my wallet through the Metamask connector, I want to display the balances of different supported chains. For example, if the supported chains array includes [polygon, base, optimism], I can only retrieve the overall account ba ...

Enhancing JSON data: Transforming basic JSON structure into more complex format

I am currently working on a typescript file that is receiving a JSON response from an external API. I am in need of assistance to convert the received JSON into a different format. Could someone please help me with this JSON conversion task? Sample JSON d ...