Issue
A lesser known built-in interface in TypeScript is VoidFunction
, which supposedly represents any function that takes no parameters and returns void. An even lesser known one is NewableFunction
, which sounds like it should represent any function that is "newable" or "constructable" (probably with a construct signature like (new (...) => ...
), but when you try to use this interface, you get unexpected behavior.
Try taking the parameters, for example. With Parameters<VoidFunction>
, you get the expected []
:
// Makes sense
type VoidParams1 = Parameters<VoidFunction>;
// ^? []
and using ConstructorParameters
gives you an error, since VoidFunction
is not constructable. However, both Parameters
and ConstructorParameters
don't accept NewableFunction
:
//@ts-expect-error Is newable, not a regular function
type CtorParams1 = Parameters<NewableFunction>;
// What?
type CtorParams2 = ConstructorParameters<NewableFunction>;
// Error: ^^^^^^^^^^^^^^^
// Type 'NewableFunction' does not satisfy the constraint 'abstract new (...args: any) => any'.
which is already odd enough, but then when you use it as a type in code:
function newfn(f: NewableFunction) {}
// This line is OK
newfn(class {});
// Error on the following line
newfn(Date); // `new Date()`...?
// ^^^^
// Argument of type 'DateConstructor' is not assignable to parameter of type 'NewableFunction'.
// Types of property 'apply' are incompatible.
// ...
// Type 'new () => any' is not assignable to type '(this: any) => any'.
What's the point of NewableFunction
then, if I can't use it as a newable function? And why isn't a type like new (...args) => any
built-in already? It's quite common to see/use. Issue ms/TS#44337 doesn't seem to address these concerns...
It seems that it accepts only "strictly newable" functions - functions that can only be invoked with new
? Using newfn(Date)
throws an error, but newfn(Float32Array)
doesn't. But again, why is it designed like this? Why is this useful?
Solution
Oh well, I don't know how I missed this, but in the release notes of version 3.2, we got strictBindCallApply
, which was implemented in part by two new interfaces, CallableFunction
and NewableFunction
. VoidFunction
doesn't have anything to do with this.
function foo(a: number, b: string): string {
return a + b;
}
let a = foo.apply(undefined, [10]); // error: too few argumnts
let b = foo.apply(undefined, [10, 20]); // error: 2nd argument is a number
let c = foo.apply(undefined, [10, "hello", 30]); // error: too many arguments
let d = foo.apply(undefined, [10, "hello"]); // okay! returns a string
Here's what they had to say about these new interfaces:
This is achieved by introducing two new types,
CallableFunction
andNewableFunction
, inlib.d.ts
. These types contain specialized generic method declarations forbind
,call
, andapply
for regular functions and constructor functions, respectively. The declarations use generic rest parameters (see #24897) to capture and reflect parameter lists in a strongly typed manner. InstrictBindCallApply
mode these declarations are used in place of the (very permissive) declarations provided by type Function.
So they're not really meant to be used by us, the regular users of TypeScript...
Answered By - caTS
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.