Automatic type inference for TypeScript getters

It appears that Typescript infers field types based solely on the private variable of a field, rather than taking into account the getter's return type union (1) or inferring from the getter itself (2):

    test('field type inference', () => {
  class A {
    x_: number;

    // 1: No type checking for the getter
    get x(): number | undefined {
      if (this.x_ === 1) return undefined;
      return this.x_;
    }

    set x(v: number | undefined ) {
      this.x_ = +v;
    }
  }

  const a = new A();
  a.x = 1;

  // 2: Inferred type is number (based on x_, not getter)
  const x: number = a.x;

  console.log(a.x) // outputs 'undefined'
})

Questioning if this behavior is specified or expected?

Note that even with strictNullCheck, these issues are not caught. It only detects missing initialization and setter errors.

An updated example to fix warnings with strictNullCheck:

test('field type inference', () => {
  class A {
    x_: number;

    get x(): number | undefined {
      if (this.x_ === 1) return undefined;
      return this.x_;
    }

    set x(v: number | undefined ) {
      this.x_ = +(v ?? 0);
    }

    constructor(value: number) {
      this.x_ = value;
    }
  }

  const a = new A(2);
  a.x = 1;

  const x: number = a.x;

  console.log(a.x)
});

Answer №1

Initially, when working with properties in TypeScript, the assumption is that reading from a property will retrieve the most recently written value for that property. However, your getter/setter setup breaks this convention. Despite this, TypeScript does not detect any issues because each get/set function is technically type-safe on its own.

Additionally, when you assign a constant to a property typed as a union, TypeScript remembers which member of the union applies for the remainder of that scope. The logic within the getters/setters does not affect this behavior.

This behavior is related to the public property, not the private one.

For example, consider this simple class:

// No getters/setters
class B {
  x: number | undefined
}

const b = new B()
b.x // number | undefined
b.x = 1
b.x // number

In this case, TypeScript remembers the assigned value, knows it is not undefined, and updates the resulting type of the property where it can determine this to be true.


Now, let's look at this class:

// Useless getters/setters
class C {
  get x(): number | undefined {
    return undefined;
  }

  set x(v: number | undefined ) {
    // no-op
  }
}

const c = new C()
c.x // number | undefined
c.x = 1
c.x // number

Here, the getters/setters are considered useless but still yield unexpected results.

Playground


According to TypeScript, a getter should return the last set value if that value was set in the same synchronously executed scope. Most programmers would likely expect the same behavior.

While ideally the compiler would flag an issue in this scenario, ensuring type safety through these setters is actually quite complex.

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

Dealing with TS error - The target demands 2 elements, while the source might contain fewer items

As a newcomer to TS, I'm struggling to grasp why TS believes that Object.values(keyCodeToAxis[keyCode]) could potentially result in an array with less than 2 elements. type ControlKey = "KeyQ" | "KeyW" | "KeyE" | "Ke ...

Exploring FileReader in conjunction with React and Typescript

I am facing an issue while trying to upload a JSON file using an input element of type file. When I attempt to use the onload method on FileReader in TypeScript, I receive an error message saying "Cannot invoke an object which is possibly 'null'. ...

Contrasting @Input with Dependency Injection in Angular 10

Is there a way to pass values from a parent component to a child component without using the @Input decorator? I'm thinking about creating an instance of the Parent class in the constructor (Dependency Injection) and then accessing the variable value ...

In Typescript, is there a specific type for images encoded in base64 format?

As a newbie to Typescript, I am diligently typing everything precisely as part of my learning journey. A property called lqip (low quality image placeholder) is being pulled from a CMS and should be a base64 encoded image. It's clearly a string, but ...

Tips for implementing <mat-progress-bar> in .ts file when making API service requests with Angular

I'm currently utilizing an API call to retrieve an image from a service, and I would like to display a progress bar while the image is being fetched. It seems that I need to incorporate the progress bar within the service as the image data is returned ...

Guide to setting up a trigger/alert to activate every 5 minutes using Angular

limitExceed(params: any) { params.forEach((data: any) => { if (data.humidity === 100) { this.createNotification('warning', data.sensor, false); } else if (data.humidity >= 67 && data.humidity <= 99.99) { ...

Implementing pagination for an Angular Material table using an HTTP request to set the page

How can I update the table page index when users click on next and previous buttons in an API that fetches data based on a specified Page number? It's important to note that I have already created a shared table. @Input() columns: Column[] = []; @In ...

Creation of Card Component with React Material-UI

I am facing difficulties in setting up the design for the card below. The media content is not loading and I cannot see any image on the card. Unfortunately, I am unable to share the original image due to company policies, so I have used a dummy image for ...

The object[] | object[] type does not have a call signature for the methods 'find()' and 'foreach()'

Here are two array variables with the following structure: export interface IShop { name: string, id: number, type: string, } export interface IHotel { name: string, id: number, rooms: number, } The TypeScript code is as shown below ...

How to replace/redirect the import statement in TypeScript from { X } to 'Y'

My situation involves an external library known as Y, which was installed using npm and loaded from the node_modules directory. This library is hosted on GitHub and currently being utilized in my project in the following manner: import { X } from 'Y& ...

Utilizing PrimeNG's p-dataView feature without repetitive FieldSets

Currently, I am utilizing p-dataView and I'm interested in implementing p-fieldset based on the application type. My goal is to prevent the fieldset from being duplicated when multiple instances occur. The scenario below illustrates one such case; how ...

What is the best way to store the outcome of a promise in a variable within a TypeScript constructor?

Is it possible to store the result of a promise in a variable within the constructor using Typescript? I'm working with AdonisJS to retrieve data from the database, but the process involves using promises. How do I assign the result to a variable? T ...

Creating personalized properties for a Leaflet marker using Typescript

Is there a way to add a unique custom property to each marker on the map? When attempting the code below, an error is triggered: The error "Property 'myCustomID' does not exist on type '(latlng: LatLngExpression, options?: MarkerOptions) ...

Error encountered: The combination of NextJS and Mongoose is causing a TypeError where it is unable to read properties of undefined, specifically when trying

Versions: Next.js 14.1 React 18 I am currently developing a profile section where users can update their profile information such as username, name, and profile photo. To achieve this, I have implemented a component that contains a form (using shadcn) to ...

Utilize decorators for enhancing interface properties with metadata information

Can decorators be utilized to add custom information to specific properties within an interface? An example can help clarify this: Interface for App state: export interface AppState { @persist userData: UserData, @persist selectedCompany: UserCo ...

What is a way to execute a series of requests using rxjs similar to forkJoin and combineLatest, without needing to wait for all requests to finish before viewing the results?

Consider you have a list of web addresses: urls: string[] You create a set of requests (in this instance, utilizing Angular's HTTPClient.get which gives back an Observable) const requests = urls.map((url, index) => this.http.get<Film>(url) ...

In order to utilize Node.js/Express/Typescript, it is necessary to use the

My current project involves creating a webservice using Express, with the goal of making it executable from both localhost and an AWS Lambda function via Claudia. In order to achieve this, I am aiming to separate the app configuration from the app.listen ...

What is the best way to retrieve the URL query parameters in a Node.js environment?

How can I retrieve URL query parameters in Node.js using TypeScript? Below is my attempted code: /** * My Server app */ import * as Http from "http"; import * as url from "url"; import { HttpServer } from "./HttpServer"; import { TaxCalculator } from ". ...

Trouble encountered when utilizing [ngClass] - Error occurred due to changes in expression after it has been checked

I keep encountering an error when attempting to utilize [ngClass] in my Angular project. The specific error message I receive is as follows: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ' ...

An issue has occurred: changes.forEach does not function as expected

Encountered an issue while attempting to retrieve data from Firestore using Angular/Ionic. PizzaProvider.ts getAllPizzas() { return this._afs.collection<Pizzas>('pizzas', ref => ref); } pizzas-list.ts pizzas: Observable<any[]& ...