question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

In JS, there's no way to assert that a property is definitely assigned

See original GitHub issue

Keywords: JSDoc, Salsa, JavaScript, definite, initialization, initialized, assigned, assertion

class ElementWrapper {
  constructor() {
    this.init();
  }

  init() {
    this.element = document.createElement('div');
  }

  getElementStyle() {
    this.element.style // error: element is possibly undefined
  }
}

We need a way to convince TypeScript in strictNullChecks that this.element is initialized in getElementStyle. Basically a definite initialization assertion.

Related is #23405 which tracks non-null assertions on the expression level.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:1
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
mhegazycommented, Apr 6, 2018

@DanielRosenwasser had an idea of an /** @delayinitialized */ on the declaration…

We can also add a tag on init like /** @initializer */ to treat it like a constructor for purposes of definite assignment detection.

0reactions
clshortfusecommented, Oct 27, 2022

The only way I can define is in Subclass.prototype after the declaration:

class Superclass extends HTMLElement {
  static {
    console.log(1, 'Super Static Init')
  }

  static superStaticField = console.log(2, 'Super Static Field')

  static observedAttributes = ['foo'];

  constructor() {
    console.log(8, 'Superclass constructor: pre-super()');
    super();
    this.superConstructorVar = console.log(10, 'Superclass constructor: post-super()');
  }

  superInstanceField = console.log(9, 'Superclass Instance Field');

}

Superclass.prototype.superInstanceProp = console.log(3, 'Super post-declaration' );

class Subclass extends Superclass {
  static {
    console.log(4, 'Sub Static Init');
    
    this.prototype.a = 1; // ts-error
    Subclass.prototype.b = 2 // ts-error
  }

  static subStaticField = console.log(5, 'Sub Static Field');

  static get observedAttributes() {
    console.log(6.5, 'HTMLElement reads observedAttributes from subclass');
    return ['foo'];
  }

  attributeChangedCallback() {
    console.log(13, 'Attributes observed');
  }

  constructor() {
    console.log(7, 'Subclass constructor: pre-super()')
    super();
    this.subConstructorVar = console.log(12, 'Subclass constructor: post-super()');
  }

  subclassInstanceField = console.log(11, 'Subclass Instance Field');
}

Subclass.prototype.subInstanceProp = console.log(6, 'Sub post-declaration');


customElements.define('x-sub', Subclass);
console.log('Element added to registry');
const el = document.createElement('x-sub');
console.log('Element created');
el.setAttribute('foo', 'bar');
console.log('Attribute added');

This yields:

1 'Super Static Init'
2 'Super Static Field'
3 'Super post-declaration'
4 'Sub Static Init'
5 'Sub Static Field'
6 'Sub post-declaration'
6.5 'HTMLElement reads observedAttributes from subclass'
Element added to registry
7 'Subclass constructor: pre-super()'
8 'Superclass constructor: pre-super()'
9 'Superclass Instance Field'
10 'Superclass constructor: post-super()'
11 'Subclass Instance Field'
12 'Subclass constructor: post-super()'
Element created
13 'Attributes observed'
Attribute added

I have to use prototype because constructor() and instance class fields are too late. (I would like to append to the shadowroot based on class properties). I’ve resolved to using post-declaration at the end of the file, but defining properties at the end of the class is awkward. I would rather use the Class static initialization blocks, but Typescript will not define the type there. That’s because it rewrites that code as:

_a = Subclass;
(() => {
    console.log(4, 'Sub Static Init');
    _a.prototype.a = 1;
    Subclass.prototype.b = 2;
})();

I’m not sure why the IIFE is needed, but if it can reworked, I think it would be fine.

Playground Link

Read more comments on GitHub >

github_iconTop Results From Across the Web

Property '...' has no initializer and is not definitely assigned in ...
Just go to tsconfig.json and set "compilerOptions": { "strictPropertyInitialization": false, ... } to get rid of the compilation error.
Read more >
Property has no initializer and is not definitely assigned
Property has no initializer and is not definitely assigned. A quick fix for the most frequent error faced by Angular Developers.
Read more >
Documentation - TypeScript 2.0
The compiler checks that variables are definitely assigned by performing control flow based type analysis. See later for further details on this topic....
Read more >
Strict Property Initialization in TypeScript - Marius Schulz
The reason for the runtime error is that the username property holds the value undefined because there's no assignment to that property.
Read more >
The 10 Most Common JavaScript Issues Developers Face
Memory leaks are almost inevitable JavaScript issues if you're not consciously coding to avoid them. There are numerous ways for them to occur, ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found