An obscure built-in interface in TypeScript is VoidFunction
, meant to represent functions that take no parameters and return void. Even more obscure is NewableFunction
, which supposedly should represent functions that are "newable" or "constructable," but using this interface can lead to unexpected results.
For instance, when you try to access the parameters, Parameters<VoidFunction>
returns []
:
// Makes sense
type VoidParams1 = Parameters<VoidFunction>;
// ^? []
However, attempting to use ConstructorParameters
with VoidFunction
will result in an error since it is not constructible. Strangely, both Parameters
and ConstructorParameters
do not work with 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'.
This behavior becomes even weirder when used as a type within 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'.
It raises the question of why NewableFunction
exists if it cannot be properly utilized as intended. Why isn't there a built-in type like new (...args) => any
already available? It's a commonly seen/used pattern. Issue ms/TS#44337 fails to address these concerns...
It appears that NewableFunction
only accepts functions that can exclusively be invoked with new
. An attempt to use newfn(Date)
results in an error, while newfn(Float32Array)
does not. But why is this restriction in place? How does this limitation serve any practical purpose?