You can see a demo in this playground.
I've made a simple generic type which can represent either a variable or a function that returns a variable. But, unfortunately, it doesn't work with a typical typeof arg === 'function'
check. It produces the following error:
This expression is not callable.
Not all constituents of type '(() => T) | (T & Function)' are callable.
Type 'T & Function' has no call signatures.
Is there a way to make it work without using type guard function?
type Initializer<T> = T | (() => T)
function correct(arg: Initializer<string>) {
return typeof arg === 'function' ? arg() : arg
}
function wrong<T>(arg: Initializer<T>) {
return typeof arg === 'function' ? arg() : arg // error here
}
const isFunction = (arg: any): arg is Function => typeof arg === 'function'
function correct_2<T>(arg: Initializer<T>) {
return isFunction(arg) ? arg() : arg
}
They’re also values, and just like other values, TypeScript has many ways to describe how functions can be called. Let’s learn about how to write types that describe functions. The simplest way to describe a function is with a function type expression . These types are syntactically similar to arrow functions: console. log ( s );
Constructs a type with all properties of Type set to optional. This utility will return a type that represents all subsets of a given type. Constructs a type consisting of all properties of Type set to required.
The never type represents values which are never observed. In a return type, this means that the function throws an exception or terminates execution of the program. never also appears when TypeScript determines there’s nothing left in a union. x; // has type 'never'!
In TypeScript, generics are used when we want to describe a correspondence between two values. We do this by declaring a type parameter in the function signature: By adding a type parameter Type to this function and using it in two places, we’ve created a link between the input of the function (the array) and the output (the return value).
You can write:
type Initializer<T> = T extends any ? (T | (() => T)) : never
function correct<T>(arg: Initializer<T>): T {
return typeof arg === 'function' ? arg() : arg // works
// arg is Initializer<T> & Function in the true branch
}
const r1 = correct(2) // const r1: 2
const r2 = correct(() => 2) // const r2: number
In the original version, arg
is resolved to (() => T) | (T & Function)
in the true branch. TS apparently can't recognize for this union function type, that both constituents are callable. At least in above version, it is clear for the compiler that you can invoke arg
after a function check.
Might also be worth to create a github issue for this case in the TypeScript repository - in my opinion T & Function
should represent some (wide) type of function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With