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

Setting URL parameters in a POST request: A guide

Currently, the data in question is structured as JSON within this code snippet. However, I've received feedback indicating that it should actually be implemented as URL parameters. I'm currently facing some difficulties with modifying this to fit ...

A problem encountered in specific JavaScript code

As a newcomer to JavaScript, I have encountered an issue while trying to run this script: <html> <head> <title>Exploring javascript functionalities</title> </head> <body> <p id="demo">I ...

Tips for asynchronously modifying data array elements by adding and slicing

I am facing an issue in my vuejs application where I need to modify an array of items after the app has finished loading. My current setup looks like this: var n = 100; var myData = []; function loadMovies(n){ // async ajax requests // add items to ...

Can you provide me the steps to delete the title attribute from images in Wordpress?

My client has expressed dissatisfaction with the tooltip that appears when hovering over images in certain browsers, particularly Safari. This tooltip displays the title attribute within the img tag, which is a requirement enforced by Wordpress. Even if w ...

How to Exclude ress.css in Vuetify.js

How can I prevent ress.css from conflicting with Bootstrap and my custom styles in Vuetify? I attempted to remove it from the Vuetify directory within node_modules, but that did not resolve the issue. ...

Modifying JavaScript prototypes on the fly can lead to troublesome issues

My curiosity has been piqued by the concept of dynamically changing a constructor's prototype in JavaScript, leading me to the findings above. It appears that an already constructed instance does not inherit the properties of the newly changed protot ...

Discovering duplicates for properties within an array of objects in React.js and assigning a sequential number to that specific field

I am working with an array of objects where each object contains information like this: const myArr=[{name:"john",id:1}{name:"john",id:2}{name:"mary",id:3}] In the first 2 elements, the "name" property has duplicates with the value "john". How can I updat ...

Connect main data to sub-component

Example Vue Structure: <Root> <App> <component> Main function in main.js: function() { axios.get('/app-api/call').then(function (resp, error) { _this.response = resp.data; }) ...

Utilizing Isotope JS on your Wordpress website

I'm in the process of integrating the .js plugin, Isotope, into my Wordpress installation. It should be positioned at the bottom of this page: To achieve this, I am referring to an example on codepen: https://codepen.io/desandro/pen/mEinp The script ...

Exploring the wonders of LoopBack querying

Discovering loopback has been an enlightening experience for me. However, as I delve deeper into its functionalities, I've stumbled upon something unexpected. I noticed that when executing queries, such as using the updateAll method, if a parameter i ...

Is it possible to incorporate async await with SetState in React?

Is there a way to properly setState when needing async/await data inside it? I know it's not recommended, but I'm struggling with getting data before setting the state. Any suggestions? codesanbox: https://codesandbox.io/s/infallible-mendeleev-6 ...

Angular 2: Issue with Component not reinitializing when query parameters change

I am currently working with Angular 2 and the latest router component to create a search functionality. Upon clicking the search button for the first time, the router navigates to the search component and retrieves data from the service. However, I have no ...

Error in Compiling HTML Elements Collection<<Element>

Currently, I am developing an eCommerce application that features a popup window for users when they click on "Add to Cart." This popup allows users to select product variations and quantities before adding the item to their cart. The popup consists of a s ...

401 Access Denied - Browser extension utilizing Angular JS

In my attempt to implement login functionality within a Chrome extension using Angular, I am consistently encountering a 401 UNAUTHORIZED error. Below is the code snippet I am using: angular.module('chrome-extension') .controller('LoginCont ...

Exploring Angular 2: Incorporating multiple HTML pages into a single component

I am currently learning Angular 2 and have a component called Register. Within this single component, I have five different HTML pages. Is it possible to have multiple templates per component in order to navigate between these pages? How can I implement ro ...

"Experience the seamless navigation features of React Navigation V6 in conjunction with

Hello there, I've been experimenting with react-navigation 6 in an attempt to show a modal with presentation: "modal" as instructed on the documentation. However, I'm facing an issue where the modal is not displaying correctly and appears like a ...

Issue with updating state in SideBar.js following button click in Layout.js

Layout.js - Inside this component, there's a button that triggers the sidebar to hide or show when clicked. 'use client' import { useRef } from 'react'; import './globals.css' import Menu from '@mui/icons-material/M ...

Unable to classify mapRef.current

I am facing an issue with my react component that contains a leaflet map. TypeScript is warning me about line mapRef.current.setView(coords, 13), stating it is an "unsafe call of an any typed value" import 'leaflet/dist/leaflet.css'; import { Map ...

Warning: The React Router v6's Route component is unable to find the origin of the key props

I recently came across an error in my console and I'm unsure which list is causing it. Is there a way for me to trace back the origin of this error so I can pinpoint where to fix it? The error seems to be related to the React Router component, which ...

Navigate to a particular date with react-big-calendar

I know this might sound like a silly question, but I am new to working with React. Currently, the React-big-calendar allows users to navigate to specific dates when selected from the Month view. What I want is for the same functionality to apply when a use ...