What purpose does the NewableFunction interface serve?

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...

Playground


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?

Answer №1

Oh my, it seems I overlooked this important detail! In the version 3.2 release notes, a new feature called strictBindCallApply was introduced. This included the implementation of two new interfaces, namely CallableFunction and NewableFunction. Notably, the existing VoidFunction is unrelated to these changes.

function foo(a: number, b: string): string {
  return a + b;
}
let a = foo.apply(undefined, [10]); // error: too few arguments
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

Their explanation sheds light on the purpose of these new interfaces:

An enhancement was made through the introduction of CallableFunction and NewableFunction types in lib.d.ts. These types offer specific generic method declarations for handling bind, call, and apply operations on regular functions and constructor functions respectively. By utilizing generic rest parameters, parameter lists can now be captured and reflected with strong typing. When operating in strictBindCallApply mode, these declarations replace the less strict ones from the original Function type.

Evidently, these interfaces are intended for internal usage rather than by typical TypeScript users like us...

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

Fix the TypeScript issue encountered during a CDK upgrade process

After upgrading to version 2.0 of CDK and running npm install, I encountered an issue with the code line Name: 'application-name'. const nonplclAppNames = configs['nonplclAppNames'].split(','); let nonplclAppNamesMatchingState ...

Using Visual Studio Code Build Tasks in Harmony

The documentation for Visual Studio Code includes examples of tasks.json configurations that allow for either typescript compilation or markdown compilation, but does not provide clear instructions on how to achieve both simultaneously. Is there a way to ...

Error: The "require" function is undefined and cannot be recognized in app.js on line 3

Encountering difficulties in connecting front-end HTML to a private blockchain for interacting with a smart contract. Steps completed thus far: Created a smart contract and deployed it on the private blockchain. npm install -g web3 Developed an HTML fil ...

Creating a dynamic name in TypeScript for an object to be utilized in an onClick event

If I have a TypeScript object declared in my view like this: <script type="text/javascript"> var myTSObject = Module.CustomClass('#someId'); myISObject.bind(); </script> Now, if I need to manage a click event from within the ...

Ignore any information in NestJS that is not included in the data transfer object

In my NestJS controller, I have defined a route for updating locality information. The structure of the controller method is as follows: @Put('/:id') updateLocalityInfo( @Query('type') type: string, @Body() data: EditLocalityD ...

Property discovered as a class method in Typescript

I'm experiencing a minor issue with my TypeScript code. Here's the situation: class Component { assertBoolean(): boolean { return true; } } class DummyComponent extends Component() { } const components: Component[] = [DummyCompo ...

Is there a way to adjust the height of mat-sidenav-content to be 100%?

I'm having trouble scrolling down my mat-sidenav-content to reach the bottom where my pagination is located. When I try using fullscreen on mat-sidenav-container, my mat-toolbar disappears. How can I adjust my mat-sidenav-content based on the content? ...

Utilize a fresh function in Angular to retrieve and store data from a URL into a variable

Currently, I am attempting to utilize Angular in order to retrieve data from a link upon clicking a button. As a newcomer to Angular with only 2 days experience, my knowledge is quite limited. What I aim to achieve is triggering the loading of JSON data w ...

Error: The Select2 query service is not available

I am looking to enhance the search functionality for my select2 dropdown. My goal is to trigger a service call with the search parameters once 3 characters are typed into the search field. However, when I try to select an option from the dropdown, I encou ...

Restricting types does not appear to be effective when it comes to properties that are related

I am working with a specific type that looks like this: type Props = { type: 'foo'; value: string; } | { type: 'baz'; value: number; }; However, when using a switch statement with the type property in TypeScript, the program in ...

The ngx-datatable encountered a resolution issue with its dependency tree and was unable to resolve it

I've been trying to incorporate ngx-datatables into an Angular 12 project by running the command npm install @swimlane/ngx-datatable. However, after installation, I encountered the following Errors: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to r ...

Tips for creating a unit test case for a specialized validator in angular reactive forms

Looking for guidance on creating a test case for this specific method: export class CustomErrorStateMatcher implements ErrorStatematcher { isErrorState(control: FormControl,form:NgForm | FormGroupDirective | null){ return control && control.inval ...

Tips for integrating Typescript Definition files with Visual Studio 2017

I have a challenge with my ASP.NET Core 2.0 application where I am attempting to incorporate TypeScript and jQuery. While TypeScript integration has been successful, I am facing issues with jQuery as it does not provide me with intellisense. Despite trying ...

Rearrange the parent object's structure based on the data and length of the child array

Is it possible to restructure parent data based on the child array data and its length? Should I stick with an array structure or consider changing the object array from the backend? No IDs are present in the child arrays. This is what has been accomplis ...

Encountering an uncaughtException: Error stating that the module '....nextserverapphomelibworker.js' cannot be located while attempting to utilize pino.transport in Next.js

I recently set up a Next.js project with typescript using create-next-app. Opting for Pino as the logging library, recommended by Next.js, seemed like the logical choice. Initially, when I utilized Pino without incorporating its transport functionality, e ...

"Although a generic type is compatible with a function argument for mapping, it may not work with

interface DataGeneric { value: number; } function transform<D extends DataGeneric>(data: DataGeneric[], get_value: (record: D) => number) { // No errors, works fine let values = data.map(get_value); // However, this line causes a ...

The feature 'forEach' is not available for the 'void' type

The following code is performing the following tasks: 1. Reading a folder, 2. Merging and auto-cropping images, and 3. Saving the final images into PNG files. const filenames = fs.readdirSync('./in').map(filename => { return path.parse(filen ...

TypeScript is still verifying libraries in the node_modules directory despite skipLibCheck option being

I've been encountering an issue with TypeScript and React where TypeScript continues to check libraries in the node_modules folder even though I have "skipLibCheck" set to true in my tsconfig.json file. Below is a snippet of my tsconfig.json file, wh ...

Invoking a self-executing anonymous function using requestAnimationFrame

Recently, I developed a basic 2D-tile-renderer using JavaScript and decided to convert it to TypeScript. The process went smoothly, with the only challenge being when I tried to call window.requestAnimationFrame with a callback function. Eventually, I was ...

Using Angular to dynamically set data and labels for a bar chart

My issue is with dynamically adding data to my bar chart dataset as it keeps returning undefined. Here's the current working version: public barChartData: ChartDataSets[] = [ { data: [], label: 'High' }, { data: [], label: 'Medium' ...