Exploring the Concept of Constructor Interfaces in TypeScript

I need some help with understanding constructor interfaces in TypeScript. I am new to this concept and I'm struggling to grasp how they are type checked. Let's take a look at an example from the documentation:

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
    tick();
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock");
    }
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

The docs explain the code snippet as follows:

Because the first parameter of createClock is of type ClockConstructor, it verifies that AnalogClock has the correct constructor signature when used in createClock(AnalogClock, 7, 32).

This implies that the DigitalClock and AnalogClock classes adhere to the structure defined by the ClockConstructor interface. But how does an interface describe a class constructor? Curious minds, any thoughts?

Answer №1

Let's begin with the straightforward interface provided in your example:

interface ClockInterface {
    tick();
}

This interface states that any instance of this type must have the tick method. The following two classes implement this interface:

class MyClock implements ClockInterface {
    public tick(): void {
        console.log("tick");
    }
}

let a: ClockInterface = new MyClock();
let b: ClockInterface = {
    tick: () => console.log("tick")
}

The implementation is clear, resembling other object-oriented languages for the class and offering a more unique approach for the 2nd implementation that may be easier to grasp for JavaScript developers.

While this setup works effectively, what if one desires to pass a constructor of a class as an argument for a function? This straightforward attempt won't suffice:

function constructorClock(ctor: ClockInterface): ClockInterface {
    return new ctor();
}

The issue here lies in expecting an instance of ClockInterface rather than the actual class or constructor function. To address this scenario, we can define an interface specifically for the class itself rather than its instances:

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}

With this, a revised function becomes feasible:

function constructorClock(ctor: ClockConstructor): ClockInterface {
    return new ctor(3, 5);
}

An additional advantage of using these builder interfaces is the ability to define static class members/methods. A prime exemplar is the ArrayConstructor:

interface ArrayConstructor {
    // Various constructor signatures along with prototype methods like isArray
}

This interface showcases multiple constructor signatures alongside supporting functions like isArray. Without the capacity to have interfaces for classes themselves instead of their instances, utilizing such functions would prove cumbersome.

In conclusion, while DigitalClock and AnalogClock adhere to implementing ClockInterface, they also conform to the ClockConstructor interface via their constructor function used with new.

I hope this elucidates some aspects better.


Edit

To clarify, the constructor doesn't return an interface but an instance adhering to the ClockInterface. To simplify further:

class BaseClock {
    // Class implementations
}

// More classes extending BaseClock

interface ClockConstructor {
    new (hour: number, minute: number): BaseClock;
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): BaseClock {
    return new ctor(hour, minute);
}

Shifting from an interface to pure classes may streamline comprehension. Would you find this approach more coherent?

The synopsis:

new (hour: number, minute: number): ClockInterface
denotes a constructor, akin to:

createClock(DigitalClock, 12, 17);

This mirrors:

function createDigitalClock(hour: number, minute: number): ClockInterface {
    return new DigitalClock(hour, minute);
}

createDigitalClock(12, 17);

The abstraction new ctor(hour, minute); (with ctor being ClockConstructor) equates to new DigitalClock(hour, minute), albeit in a more generalized manner.

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

Can you explain the process of a put request in Angular, Express, and Mongoose?

My colleague and I are currently deciphering the inner workings of a code snippet generated by a tutorial. Our main focus is on how the client/server communication flows once line 8 of factory.js is triggered: factory.js app.factory('postFactory&apo ...

Identifying when a system window covers an iframe

After watching a fascinating YouTube video (https://www.youtube.com/watch?v=TzPq5_kCfow), I became intrigued by how certain features demonstrated in the video could be implemented using JavaScript. One specific question that arose for me was how one can d ...

Controlling Fabric in Three.JS

(I'm still learning so forgive me if this is a beginner question.) I'm currently working on fitting a cloth material to a character model in Three.JS. What would be the most effective approach for this? Should I create a complete garment as cloth ...

Form submission not being recognized by Ajax's .done() function

I'm trying to include a form from another file using ajax. It's a simple form that is activated by an onclick event defined as follows: <a href="javascript:void(0);" class="remitir" title="Reemitir Solicitud" data-id="'.$value['idso ...

What are the characteristics of an array?

Is there anyone who can assist me with writing a code to count the properties of an array? I have an array and I need to tally up all the complete items that have a value of 1. Below is the array: [{ "order_id": "336566", "customer_name": "joel" ...

Patience is key as you wait for the observable to finish

My methods have dependencies where one method needs to complete before the next can be called. process1(data: string) : Observable<string> { this.dataservice.process(data).subscribe( (response) => { return response. ...

The issue with pre-increment and its compatibility with setState

I'm facing an issue with the functionality of the component below: const App = () => { const [count, setCount] = useState(0); const test = () => { setCount(++count); } return ( <div> <p>You clicked {count} time ...

React: Avoid unnecessary re-rendering of child components caused by a bloated tree structure

I am dealing with a tree/directory structured data containing approximately 14k nodes. The issue I am facing is that every time a node is expanded or minimized by clicking a button, causing it to be added to an 'expanded' Set in the Redux state, ...

The array contains no elements, yet it is not empty, and when stringified with JSON.stringify, it results

I've been working on developing an event registration page for a school club I'm involved in, but I'm encountering a problem where my program is unable to correctly read the contents of an array and display each item as a ListItem within a L ...

How to Make Page Slide in Either Direction Based on User Click Location

Although the title of my question may not be very descriptive, I am essentially trying to implement a functionality where a page will slide right if a user clicks a link to the right of their current active link, and slide left if they click a link to the ...

Incorporate fresh Google sites into different web pages using iFrame integration

Wishing you a fantastic day! I am currently working on embedding a brand new Google site into another webpage. I attempted to use an iframe for this purpose, but unfortunately it did not work as expected. Here is the code snippet: <iframe width="1280 ...

Is it possible to convert a leaflet marker into a nuxt-link function?

Recently, I started using nuxt and vue-leaflet to create an interactive map, even though I am quite new to it. This map consists of multiple markers representing different locations. The goal is for the respective page to open when a user clicks on a mark ...

Troubleshooting import errors with Typescript for C3 and D3 libraries

I have recently started working on a project using the C3 graphing library within an Ionic2/Angular2 TypeScript setup. After installing C3 via npm and the type definitions via tsd, I imported it into my own TypeScript file like this: import {Component} fr ...

Cannot display GIF file from the SRC directory in a React application

I am trying to display a gif from the "/src/images" folder in my component, but I keep getting my "old" value displayed instead. What could be causing this issue? Snippet from Danke.js site: import Confetti from "../images/confetti.gif"; <Box sx={{ ju ...

Can you explain the meaning of `images:Array<Object> = [];` in TypeScript?

Recently, I stumbled upon this code snippet in TypeScript images:Array<Object> = []; I'm curious, what exactly does the "<>" notation signify? ...

How can I assign integer values to specific child elements after splitting them?

Below is the HTML code that needs modification: <div class="alm-filter alm-filter--meta" id="alm-filter-1" data-key="meta" data-fieldtype="checkbox" data-meta-key="Cate" data-meta-compare="IN" data-meta-type="CHAR"> <ul> <li class=" ...

"Customize your text alignment with TinyMCE's align

I'm in the process of updating from an outdated version of TinyMCE to the most recent release. In the old TinyMCE, if you inserted an image and aligned it to the left, the HTML generated looked like this: < img src="testing.jpg" align="left" > ...

Personalized dropdown appearance

During my work on a select box, I discovered that custom styling is not possible. I attempted to use some plugins but did not find one that met my needs. I am seeking a dropdown menu with options in both black and gray, similar to this template but using ...

What is the method for obtaining the image alt attribute using JavaScript?

I can't seem to retrieve the image alt tag using JavaScript. Is it even possible and if so, how should I go about it? Perhaps something like this: $caption.text( flkty.selectedElement ).attr('alt') (last line of JavaScript) But I'm st ...

Retrieve data attributes to obtain details about the slider before and after

My task is to create a slider with information about the previous and next slides. For example, in the slider, there are left < and right > arrows. Behind these arrows, there are circles. When you hover over any arrow to change the next or previous ...