What sets `this` apart from `static` in Typescript?

I am just beginning my journey with Typescript and I am eager to expand my knowledge. In the Official Documentation, a section on static properties caught my attention:

Static Properties

So far, we have been focusing on the instance members of a class - those that are associated with objects created from it. However, classes in Typescript can also have static members, which are associated with the class itself rather than instances of it. By using the static keyword, we can define properties or methods that are accessible directly through the class name. This is similar to how we use 'this.' for instance accesses, but with static members, we prepend the class name instead.

The code example provided illustrates this concept:

class Grid {
    static origin = {x: 0, y: 0};
        calculateDistanceFromOrigin(point: {x: number; y: number;}) {
            let xDist = (point.x - Grid.origin.x);
            let yDist = (point.y - Grid.origin.y);
            return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
        }
    constructor (public scale: number) { }
}

let grid1 = new Grid(1.0);  // 1x scale
let grid2 = new Grid(5.0);  // 5x scale

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

After making a slight modification by changing static origin = {x: 0, y: 0}; to origin = {x: 0, y: 0};, and

let xDist = (point.x - this.origin.x);
let yDist = (point.y - this.origin.y);

The output remains the same. So, what exactly is different here? Any insights would be greatly appreciated!

Answer №1

When using static properties or methods in a class, there is no difference in how you access them compared to non-static properties. The key distinction lies in the fact that static properties are shared across all instances of the class.

In the example below, we define a class called Grid with a static property named origin:

class Grid {
    static origin = {x: 0, y: 0};
}

console.log(Grid.origin); // {x: 0, y: 0}

This allows us to access the origin property without having to create an instance of the Grid class. If origin were not declared as static, then we would need to instantiate a new object to access the value:

class Grid {
    origin = {x: 0, y: 0};
}

const grid = new Grid();
console.log(grid.origin); // {x: 0, y: 0}

It's important to note that static properties belong to the class itself rather than individual objects created from the class. This means any changes made to a static property will be reflected across all instances of the class:

class Grid {
    static origin = {x: 0, y: 0};

    constructor() {
        console.log(Grid.origin);
    }
}

console.log(new Grid()) // {x: 0, y: 0}
console.log(new Grid()) // {x: 0, y: 0}

Grid.origin = {x: 5, y: 6}

console.log(new Grid()) // {x: 5, y: 6}
console.log(new Grid()) // {x: 5, y: 6}

If you require different values for each object instance, avoid using static properties. Instead, opt for regular instance properties.


It's generally best practice to separate static and instance properties/methods within a class. However, in cases where only one instance of a class is needed, making everything static can simplify management:

class ScoreTracker {

    private static _score: number = 0;

    public static setScore(value: number): void {
        ScoreTracker._score = value;
    }

    public static getScore(): number {
        return ScoreTracker._score;
    }

    public static addPoints(value: number): void {
        ScoreTracker._score += value;
    }

    public static removePoints(value: number): void {
        ScoreTracker._score -= value;
    }

}

By keeping score-related operations within the ScoreTracker class as static methods, we ensure that score management is centralized:

ScoreTracker.setScore(10);
ScoreTracker.addPoints(1);
ScoreTracker.removePoints(2);
console.log( ScoreTracker.getScore() ); // 9

Hopefully this explanation clarifies the usage of static properties and methods in classes.

Answer №2

When you try to access the Grid.origin in a non-static context, it should ideally result in a crash according to OOP principles. However, TypeScript acts as a middle-man between developers and JavaScript, lacking true OOP support and knowledge of keywords like "static".

To solve this issue, ensure that you properly declare the member as static. Even though the code may still work when executed, your IDE should flag this violation because it understands the concept of static members.

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

An array in Typescript containing elements of type MyType<T> with T being different for each element

TL;DR Is there a way to create an array of tuples where the second element is automatically derived from the first? Check out the example playground to see the code below in action. My scenario I'm using a tool called Puppeteer to navigate through ...

The concept of inheritance in TypeScript

Can Typescript include inheritance features along with overloading, overriding, and other functionalities? ...

How can TypeScript and MobX work together using Set()?

I'm encountering errors while trying to implement Set() in my Grocery Shopping app using MobX with TypeScript. I have a simple setup with defined types in types.ts: types.ts export type Item = { name: string; instock: number; price: number; }; ...

Connecting the mat-progress bar to a specific project ID in a mat-table

In my Job Execution screen, there is a list of Jobs along with their status displayed. I am looking to implement an Indeterminate mat-progress bar that will be visible when a Job is executing, and it should disappear once the job status changes to stop or ...

Component throwing error when receiving an array in React (TypeScript)

Encountering an issue when trying to specify inputs for Component 2. Seeking guidance on how to properly pass and describe arrays input. Data Model: interface IIncident { id: string, title: string } Component 1: interface IncidentManagerProps { ...

I am looking to dynamically print countries from an array in my code based on the selected option

I'm in need of some simple code assistance. I want to display a list of countries that correspond to a selected letter, but I'm struggling to do so dynamically at the moment. For example, if I select the letter A from a dropdown menu, I want the ...

Error: Attempting to access property 'toLocaleLowerCase' of an undefined value

I've been working on a custom pipe following the instructions carefully, but I keep encountering an error when trying to filter my list. Below is the code for my custom pipe: import { Pipe, PipeTransform } from '@angular/core' ...

What could be the reason behind TypeScript's inability to accurately display the return type?

I am struggling to make the following code behave as expected without using: as const function overloading (if impossible to achieve with arrow functions) const f = <S extends string>(args: { str?: S }) => { return { a: args.str || 123, ...

Unable to retrieve the data property from the Axios response object following a successful GET request: The property 'data' is not present in the type 'void'

Currently, I am working on a personal project using React and TypeScript to enhance my skills. However, I have encountered a puzzling error in the following code snippet, which involves using Axios to fetch data: const fetchItem = async () => { const ...

Solving commitments through a series of actions

Can someone please explain why when resolving promises in a loop, accessing the loop variable is necessary for it to work correctly? Here's an example where logging occurs 5 times: for (let i = 0; i < 5; i++) { this.getData() .then(() ...

Utilizing the "or" operator in Typescript alongside Omit<> - A Comprehensive Guide

In the following code snippet: type WithOr = {a:string} & ({b?:true, c?:never} | {b?:false, c:number}) type Ommited = Omit<WithOr, 'a'> const m1: Ommited = {b: true}; const m2: WithOr = {...m1, a: 'a'} An error will be encou ...

Error in Typescript: The module '@azure/functions' does not have an exported member named 'app'

Let's dive into a TypeScript and Azure integration question: Within my Node.js code for an Azure function: import { app, HttpRequest, HttpResponseInit, InvocationContext, } from "@azure/functions"; import { workerExec } from ". ...

Angular 8 fails to retain data upon page refresh

I have a property called "isAdmin" which is a boolean. It determines whether the user is logged in as an admin or a regular user. I'm using .net core 2.2 for the backend and Postgre for the database. Everything works fine, but when I refresh the page, ...

Oops! ExcelJs is throwing an error: "Unable to merge cells that are already

Currently, I'm attempting to create an Excel sheet using excelJs. Each order has an array of order items, which could be one or more. My goal is to avoid repeating certain information within the order while iterating through each item in the order. Ho ...

Error encountered while installing Material UI in Next.js with TypeScript and pure JavaScript configurations

I'm brand new to React and Next.js, so please forgive me for asking what may seem like a silly question. I'm attempting to install Material UI in a fresh Next.js application that I created using "npx create-next-app@latest". I've been refere ...

Struggling to grasp the concept of call signatures

I'm a bit confused about call signatures in Typescript. I've come across them and read some information, but I'm still not clear on what exactly they do. The Typescript documentation explains: When we want to describe something that is cal ...

Extract pieces from a union type that includes a discriminator which is itself a union

My current type declaration looks like this: enum Type { A = 'A', B = 'B', C = 'C' } type Union = | { type: Type.A | Type.B; key1: string } | { t ...

Error: JSON parse error - unexpected character 'a' at index 1

I'm encountering an issue while attempting to change the album title from "cars" to "car". The error message I keep receiving is: SyntaxError: Unexpected token a in JSON at position 1. Any ideas on what might be causing this problem? Below is the cu ...

Leveraging an abstract class in Typescript for easy function sharing

When working with Typescript, I encountered a situation where I had 2 functions containing the same function code. To streamline my code, I decided to extract this common function into its own file. Currently, I have implemented it as shown below. However ...

Should private members be kept confidential during program execution?

While Typescript's "private" members may not be truly private at runtime, traditional closures maintain the privacy of their members. Is there value in ensuring that private members remain private during runtime? ...