Fix - Not all constituents of type 'X | Y' are callable (TS)

avatar

Borislav Hadzhiev

Sat Mar 05 20223 min read

banner

Photo by Verne Ho

Fix - Not all constituents of type 'X | Y' are callable (TS) #

The error "This expression is not callable. Not all constituents of type 'X | Y" are callable" occurs when a value might be of multiple types, some of which are not functions. To solve the error, use a type guard to make sure the value is a function before calling it.

Here is an example of how the error occurs:

index.ts
// 👇️ returns string OR function function example() { return [ 'hello', function sum(a: number, b: number) { return a + b; }, ]; } // 👇️ sum is string OR function const [str, sum] = example(); // ⛔️ Error: This expression is not callable. // Not all constituents of type 'string | ((a: number, b: number) => number)' are callable. // Type 'string' has no call signatures.ts(2349) console.log(sum(15, 30));

The function returns an array containing a string and a function.

The problem here is that the function's return value is typed as (string | ((a: number, b: number) => number))[], in other words - an array containing strings or functions.

Types that are formed by combining two or more other types are called union types in TypeScript.

We need a way to tell the compiler that we are sure the value we are trying to call is a function.

To solve the error in this specific scenario, use a const assertion.

index.ts
// 👇️ returns tuple with 1st element string // and second element function function example() { return [ 'hello', function sum(a: number, b: number) { return a + b; }, ] as const; // 👈️ const assertion } // 👇️ sum is function const [, sum] = example(); // ✅ works as expected console.log(sum(15, 30));

The const assertion changed the function's return value from an array containing string or function elements to a tuple containing a string first element and a function second element.

An alternative approach is to use a type guard and make sure the value is a function before calling it.

index.ts
function example() { return [ 'hello', function sum(a: number, b: number) { return a + b; }, ]; } // 👇️ sum is string or function const [, sum] = example(); if (typeof sum === 'function') { // 👇️ sum is function here // ✅ works as expected console.log(sum(10, 15)); }

You might also get the error when using a union type in an interface or a type alias.

index.ts
interface Person { [key: string]: string | ((a: number, b: number) => number); } const person: Person = { sum(a: number, b: number) { return a + b; }, }; // ⛔️ Error: Not all constituents of type //'string | ((a: number, b: number) => number)' are callable. person.sum(100, 200);
The example above uses a union type and an index signature to specify that the Person type might have properties with any name, as long as they have a type of string or a function.

TypeScript warns us that the property in the object might have a type of string, so we can't directly invoke it without making sure it is a function.

To get around this, use the typeof operator and check if the value is a function.

index.ts
interface Person { [key: string]: string | ((a: number, b: number) => number); } const person: Person = { sum(a: number, b: number) { return a + b; }, }; if (typeof person.sum === 'function') { person.sum(100, 200); }
A much better solution is to be more specific and set a specific property name to the type, because you might have multiple functions in the interface that take different types of parameters.
index.ts
interface Person { [key: string]: string | ((a: number, b: number) => number); sum: (a: number, b: number) => number; // 👈️ added this } const person: Person = { sum(a: number, b: number) { return a + b; }, }; // ✅ Works as expected console.log(person.sum(10, 20));

Now TypeScript knows that the sum property on the object will be a function and it couldn't be a string.

Use the search field on my Home Page to filter through my more than 1,000 articles.