microsoft / microsoft/TypeScript

Property initializers output for non-initialized types

Open
#45,076 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Docs
Dominant language
Go
Stars
111k
Forks
14.4k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

Bug Report

This is technically not a bug, given ES Spec, however, I'm filing an issue in case an consideration needs to be made on how to handle with regard to documentation, etc, and/or to provide a solution for people facing the same.

Issue Summary

  • ESNext spec seems to call for defining all class fields. This also affects those which are optional or specified with a boom.
  • Spec also dictates that property initializers are called after the super call. (https://github.com/tc39/proposal-class-fields)

This results in any properties specified in the inherited class that are assigned during the base class constructor to be overwritten, even if they do not have initializer values specified in the inherited class.

The behaviour which causes the error can be seen below:

class A {
  a?: string
}

Output is:

// target = ESNext
class A {
  a; // Outputs a statement without initialized value, which is treated as a = undefined;
}

// target = ES2020
class A {
}

Here is a simplified version of how this affected me

abstract class Base<T extends Record<string, any>> {
  // Single constructor for all derivatives
  constructor(o: Omit<T, typeof Base>) {
    Object.assign(this, o);
  }
}

class A extends Base<A> {
  myProp?: string
  myProp2!: string
}

const a = new A({ myProp2: 'hello' });

// ESNext: a = { myProp2: undefined, myProp: undefined }
// ES2020: a = { myProp2: 'hello' }

The above produces:

// TypeScript target=ESNext
class A extends Base {
  myProp;
  myProp2; // Note the boomed property still gets output here, so it is initialized after the super call - which maybe surprising behaviour
}

// ES2020 / Babel
class A extends Base {
}
🔎 Search Terms
  • property initializers
🕗 Version & Regression Information

TS 4.3.5

Solution

For those facing this issue, simply change property declarations to ambient.

ie:

class A extends Base<A> {
  declare myProp?: string
  declare myProp2: string
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimal class examples in the issue and compare the emitted output for the ESNext and ES2020 targets, including optional and definite-assignment properties. Determine whether the intended outcome is a compiler change or documentation, then verify that the chosen behavior avoids overwriting values assigned by the base constructor.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.