Exploring nested static properties within TypeScript class structures

Check out this piece of code:

class Hey {
  static a: string
  static b: string
  static c: string
  static setABC(a: string, b: string, c: string) {
    this.a = a
    this.b = b
    this.c = c
    return this
  }
}
class A {
  static prop1: Hey
  static setFields() {
    this.prop1 = Hey.setABC('aee', 'bee', 'cee')
    return this
  }
}
A.setFields()
console.log(A.prop1.a)

This TypeScript code results in a compile time error:

Property 'a' does not exist on type 'Hey'. Did you mean to access the static member 'Hey.a' instead?

In JavaScript, however, if you try the following snippet, it should work fine:

"use strict";
class Hey {
    static setABC(a, b, c) {
        this.a = a;
        this.b = b;
        this.c = c;
        return this;
    }
}
class A {
    static setFields() {
        this.prop1 = Hey.setABC('aee', 'bee', 'cee');
        return this;
    }
}
A.setFields();
console.log(A.prop1.a);

Give it a go in the TypeScript playground.

I attempted to typecast using:

console.log((A.prop1 as Hey).a);

Yet, I faced the same issue :(

Why does TypeScript restrict this when it works perfectly in JavaScript?

Answer №1

You just realized that prop1 has the incorrect type. It should actually be defined like this:

class A {
    static prop1: typeof Hey;
    ⋯
}

The type Hey refers to the instance type of the class Hey, while typeof Hey refers to the type of the constructor value of Hey. Since static properties exist on the constructor, not on instances, prop1 should be of type typeof Hey, not Hey.

This distinction between types and values in TypeScript can be confusing because they exist in separate namespaces. Therefore, having a value named X and a type named X doesn’t necessarily mean they are related. This means that typeof X is generally different from X, requiring careful consideration. Check out this answer to a similar question for more details.

Once you make this correction, everything should work smoothly:

A.setFields()
console.log(A.prop1.a); // works fine

Keep in mind that the issue was somewhat hidden due to your Hey class not having any instance members. An instance of Hey is essentially identical in the type system to the empty object type {}, allowing almost anything to be assigned to it. You may want to avoid using empty classes as they behave strangely in TypeScript. If a class exists, it should ideally have instance members. If Hey had even one random property, you would have encountered this error:

class Hey {
    ⋯
    oops = 0; // actual property
}

class A {
    static prop1: Hey;
    static setFields() {
        this.prop1 = Hey.setABC('aee', 'bee', 'cee'); // error!
        //           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Property 'oops' is missing in type 'typeof Hey' but required in type 'Hey'.
        return this
    }
}

If you never plan on creating instances of your class, plain objects might provide the desired behavior instead. For example:

const Hey = {
    a: "",
    b: "",
    c: "",
    setABC(a: string, b: string, c: string) {
        this.a = a
        this.b = b
        this.c = c
        return this
    }
}
const A = {
    prop1: Hey,
    setFields() {
        this.prop1 = Hey.setABC('aee', 'bee', 'cee')
        return this
    }
}
A.setFields()
console.log(A.prop1.a); // okay

This setup behaves similarly to your previous code. One key difference is that I initialized your properties whereas you left them undefined. However, leaving properties uninitialized could lead to potential null/undefined errors. Although TypeScript has strict initialization checks for class instances with --strictPropertyInitialization, this feature does not yet cover static members (requested at microsoft/TypeScript#27899).

You are not obligated to refactor away from classes, but TypeScript’s support for empty classes with only static members is limited, as you’ve experienced.

Check out the code in the Playground

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

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

The regex pattern did not match the line of code in the Node.js script

I need help finding a regex pattern that can identify any line of code containing just one reference to a core module. Here is an example: const coreModuleMatches = /'^[var|const]{0,1}[a-z\$\_]{1,}=require([\'|"][assert|fs|path][ ...

Changing the fill color of an SVG pattern remains unchanged

I have been working with Vue.js to create SVGs with shape patterns as background. The patterns themselves are functioning correctly, but I am encountering an issue when attempting to dynamically change the color of the pattern filling by passing props. D ...

What is causing the error message "Uncaught TypeError: Cannot read properties of null (reading 'push')" to be displayed when running this code?

I encountered an issue while trying to run this code which resulted in an Uncaught TypeError: Cannot read properties of null (reading 'push') console.log("HI"); let addbtn = document.getElementById("addbtn"); addbtn.addEventLi ...

Encountering a bindings issue when trying to utilize libxml-xsd within an electron application

Seeking guidance on validating an XML file against its schema within an electron application. Prior to adding the 'libxml-xsd' require statement to my angular2 service, it functioned properly. However, upon inclusion of this statement: const xs ...

Exploring through objects extensively and expanding all their values within Angular

I am in need of searching for a specific value within an object graph. Once this value is found, I want to set the 'expanded' property to true on that particular object, as well as on all containing objects up the object graph. For example, give ...

Distribute your SolidJS Typescript components on npm

Recently, I developed a SolidJS TypeScript UI component and successfully published it to the npm registry. The project structure is organized as follows: |-app |-index.html |-src |-index.tsx |-App.tsx |-utils |- ... |-com ...

Experiencing an issue in Test Cafe when attempting to click on an invisible link using the Client Function

I need to find a way to click on an invisible button in HTML. I attempted to use ClientFunction, however I encountered an error related to the element. import { Selector,ClientFunction } from 'testcafe'; fixture('Clicking Invisible link&apo ...

The Facebook SDK fails to activate in Internet Explorer

I am currently working on implementing a Facebook login using the JavaScript SDK. Everything is functioning correctly in most browsers, but I am experiencing issues with certain versions of Internet Explorer. The login functionality is not working on my l ...

Tab knockout binding

I have a section in my HTML with 2 tabs. The default tab is working properly, but when I attempt to switch to the other tab, I encounter an error. Can anyone provide assistance in determining why this error occurs? Here is the HTML code: <ul class="na ...

Showing particular URL text upon opening a new window using JavaScript

I've encountered an intriguing scenario. In my application, there's a feature that triggers a new window/tab to open when a button is clicked. Upon opening, a predefined HTML page is shown to the user with a specific URL set. I'm curious abo ...

Having trouble with importing a variable in an Express application? You may encounter this error message: "Route.get() must

When trying to import requireSignin from the controllers/auth.js file into the routes/user.js file and adding it to the router.get('/user/:id', requireSignin, read); route, an error occurs: Error: Route.get() requires a callback function but r ...

Exploring AngularJS ng-repeat features for custom attribute settings

I'm currently facing a challenge in removing multiple repetitive divs by utilizing ng-repeat. <!-- I have 21 of these --> <div class="table-row"> <span class="glyphicon glyphicon-wrench"></span> <label>Chlo ...

Having trouble capturing the CHANNEL_ANSWER event in my node.js script when communicating with Freeswitch

Greetings to all! I have a query regarding freeswitch. Although my experience with freeswitch is limited, I am currently working on connecting my node js script to the freeswitch server. So far, I have managed to establish a successful connection and gene ...

The exploration of child routes and modules

I'm currently working on a somewhat large project and I've decided to break it down into modules. However, I'm facing an issue with accessing the routes of admin.module.ts. In my app.module, I have imported the admin Module. imports: [ Br ...

Adjust the value based on selection

I aim to display another div when either the Full Time or Part Time option is selected. Additionally, I would like to calculate a different value for each option; if 'Part Time' is chosen, PrcA should change to PrcB. Here is the code snippet tha ...

Stop ajax request when select2 is clicked

Is there a way to change the ajax call behavior in select2 drop down items so that it only retrieves data when I start typing in the search box, and not on click of the element? Your guidance on this issue would be highly appreciated. $("#ddlItems").sel ...

Adjust the path of an element as it decreases in size

Sorry for the weird title, but that's likely why I can't find the solution on my own. I'm experimenting with CSS animations and I want the form element to decrease in height from 100px -> 0, but I want it to collapse from the top to the ...

$filter is functioning correctly, however it is generating an error message stating: "Error: 10 $digest() iterations reached. Aborting!"

Here is an example of a JSON object that I am working with: { "conversations":[ { "_id": "55f1595d72b67ea90d008", "topic_id": 30, "topic": "First Conversation", "admin": "<a href="/cdn-cgi/l/e ...

Employing ng-click within a displayed column of Datatable

Currently, I am utilizing a plain Datatable in AngularJS without any Angular-Datatable libraries. Everything appears to be working well except for one issue. Here is the datatable code snippet: $scope.siInfoTable = $('#siInfoTable').DataTable({ ...