Tips on using constructor functions and the new keyword in Typescript

This is a demonstration taken from the MDN documentation showcasing the usage of the new keyword

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

const car1 = new Car('Eagle', 'Talon TSi', 1993);

In TypeScript, the equivalent version of the function might look like:

/* Car type */
type CarDetails = {
    make: string;
    model: string;
    year: number;
}

/* Setting "this" to be of type CarDetails */
function Car(this:CarDetails, make: string, model:string, year:number) {
  this.make = make;
  this.model = model;
  this.year = year;
}

Although this approach provides IntelliSense/autocompletion while writing the code, it does not infer types when creating an instance using the new keyword. Even after incorporating TypeScript into the method, instantiating an object like this:

const car1 = new Car('Eagle', 'Talon TSi', 1993);

still results in the type of car1 being any

How can I ensure that the type of car1 is inferred as the this type of the Car method?

(without explicitly specifying the type for the instance object, such as

const car1: CarDetails = new Car(...;
)

Answer №1

It's clear that using the class syntax is the preferred approach, but like many others, I found myself going in circles trying to make the old valid JS syntax work. After much searching, I stumbled upon a helpful approximation in this obscure answer, which wasn't very well explained in the official Typescript documentation. Here's a modified snippet of the relevant code:


// Only instance members here.
export interface Circle { // Naming the interface after the variable
  radius: number;
  area: () => number;
  perimeter: () => number;
}

// Potential for defining static members here.
interface CircleConstructor {
  new(radius: number): Circle;
}

export const Circle = function(this: Circle, radius: number) {
  const pi = 3.14;
  this.radius = radius;
  this.area = function () {
    return pi * radius * radius
  }
  this.perimeter = function () {
    return 2 * pi * radius;
  }
} as unknown /*as any*/ as CircleConstructor; // Pay attention to the trust-me casting
    
const c = new Circle(3); // All good

However, there are some drawbacks to this solution. For example, it uses the any type which is generally discouraged, and it relies heavily on the as operator which can be considered unsightly. While switching to unknown instead of any is an improvement, the fundamental issue persists. Nevertheless, using unknown silences linting errors, making it a step up from using any.

As of now, this is the most effective workaround I've come across. The Typescript team is aware of this dilemma, but they have chosen not to support this kind of "syntax".

Answer №2

Why not consider using classes instead?

class Vehicle {

  brand: string;
  type: string;
  productionYear: number;
  
  constructor(brand: string, type: string, year: number) {
    this.brand = brand;
    this.type = type;
    this.productionYear = year;
  }
}

// Implementation remains the same

const vehicle1 = new Vehicle('Toyota', 'Camry', 2019);

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

Transferring an array to PHP using AJAX

In this coding scenario, I initialize my array: let myArray = []; Within a loop, elements are added to the array as follows: myArray.push($(this).data('id')); // Example: [1, 2, 3, 4] Subsequently, I transmit this data to PHP using AJAX throu ...

Retrieve the HTML value of an element in Vue.js by clicking on its adjacent element

Hey there, I'm currently working on a simple notes app and I've hit a roadblock with one particular feature. In my project, I have a card element with a delete button as a child. What I need to achieve is to check if the value of the .card-title ...

Is it necessary to compile Jade templates only once?

I'm new to exploring jade in conjunction with express.js and I'm on a quest to fully understand jade. Here's my query: Express mentions caching jade in production - but how exactly does this process unfold? Given that the output is continge ...

Running a JavaScript script within MongoDB

Seeking guidance on running a JavaScript file within MongoDB. Here is a snippet of code from my JS file: function loadNames() { print("name"); } My attempt to execute the file from the command prompt: mongo test.js resulted in the following error: ...

I am puzzled as to why I am still facing the error message: "'node' is not recognized as an internal or external command, operable program or batch file."

I'm in the process of setting up typescript for a new node project. Here are the steps I've taken so far: Installing typescript: npm i --save-dev typescript Installing ts-node: npm i --save-dev ts-node Installing the types definition for node ...

Utilizing React Typescript Discriminating Unions to choose between two different types based solely on props

In my project, I have a component that consists of different types: type Base = { color: string } type Button = { to: string } & Base type Link = { link: string linkNewTab: boolean } & Base type ComponentProps = Button | Link e ...

Having trouble initiating the server using npm start

Just starting out with nodeJs: I have created a server.js file and installed nodejs. However, when I try to run "npm start", I encounter the following errors. C:\Users\server\server.js:43 if (!(req.headers && req.headers. ...

D3 Chart: What is the best way to insert images into a node?

Within the jsFiddle demo provided in this query, there is code displayed. I am curious about how I can assign images to each node. var graph = { "nodes":[ {"name":"1","rating":90,"id":2951}, ] } You can access my jsFiddle Demo through this ...

Incorporate a unique identifier for dynamic elements

Is there a way to give generated divs the same name so I can markup them easily? $.getJSON("data/reviews.json", function(data){ for(var i=0; i<data.length; i++) { var review = sym.createChildSymbol("Template", "content"); review.$("title").html ...

Using Vue.js to bind labels and radio buttons in a form

My goal is to generate a dynamic list of form polls based on the data. However, using :for or v-bind:for does not result in any HTML markup appearing in the browser, causing the labels to not select the corresponding input when clicked. I have prepared a J ...

What is the method for identifying the environment within an Express.js application?

Is there a reliable method for determining the environment in which an expressJS app is currently operating (development, test, production)? I have checked process.env, but found no clear indication of the environment. I know that variables can be set in ...

If a specific class is identified, add a border to the div when clicked using JavaScript

Is there a way to use javascript/jquery to add a border to a selected div? I have multiple rows with columns, and I want only one column per row to be highlighted with a border when clicked. Each row should have one column with a border, so it's clear ...

When a class and ID are dynamically applied, the click event may not fire and the CSS may not be applied

Hey everyone, I am facing an issue with declaring id and class when creating a table dynamically. I have tried multiple functions to make my click event work but without success. Can anyone help me with this? Below is the code for my dynamic table where I ...

Unable to store loop information fetched from api into a JSON file

I'm currently facing an issue with my code where I am trying to save the results from looping through an API into a JSON file using writeFileSync. Here is the code snippet in question: function processRarity(limit) { var resultJson = []; for ( ...

Use a conditional statement for each element within the array

Below is the code I am currently using: if (res[0].toString() == "hello") { res[0] = "string"; }; While it works, I would like this logic to apply to all elements rather than just the first one. Is there a way to achieve this for every element in the ar ...

Exploring the process of retrieving parameters within a component using React Router DOM version 4

I am struggling with the following code: <BrowserRouter> <Route path="/(:filter)?" component={App} /> </BrowserRouter> In previous versions of React Router, wasn't the filter param or '' on the root supposed to be in ...

Utilizing external functions within AngularJS Controller

I need to execute an external JS function that fetches data from a REST endpoint, which takes some time. The problem is that the graph is loading before the data is retrieved and inserted into it. External JS: function callEndpoint() { var sensorID = ...

Issue with Tailwind classes not applying correctly upon refreshing the page in production settings

Challenge Description: Encountering an issue with my nextjs project that utilizes tailwindcss. The login page initially loads with the required classes for the UI, but upon refreshing the page, the classes disappear from the DOM, causing a broken UI. Her ...

What could be causing the malfunction of my token rotation feature in nextAuth?

I am developing a web application that involves working with an external API alongside my team member. We are making API requests using Next.js. I have implemented nextAuth for authentication, but I am facing issues with token rotation. After successful lo ...

Silky animations in kinetic.js (html5 canvas)

I am seeking a better grasp on kinetic.js animation. Recently, I came across a tutorial at . After experimenting with the code provided, I managed to create an animation that positions my rectangle at x coordinate 100. However, I am struggling to achieve s ...