Disallow uninitialised property overrides by sandersn · Pull Request #33423 · microsoft/TypeScript (original) (raw)

This PR is part of the migration from [[Set]] semantics to [[Define]] semantics. #27644 tracks the complete migration. This is the third step.

Briefly, uninitialised property declarations that must now use new syntax when the property overrides a property in a base class. The new syntax is only allowed in this case and is an error elsewhere. That's because when Typescript supports [[Define]] semantics, it will start emitting uninitialised property declarations, where previously it did not. This will be a major breaking change:

class B { p: number } class C extends B { p: 256 | 1000 // use whatever value you need here; it }

Previously this emitted

class B {} class C extends B {}

When Typescript supports [[Define]] semantics, it will instead emit

class B { p } class C extends B { p }

which will give both B and C and property p with the value undefined. (This is an error today with strictNullChecks: true.)

The new syntax will cause Typescript to emit the original JS output:

class B { p: number } class C extends B { declare p: 256 | 1000 }

This PR adds an error prompting the author to add the new syntax in order to avoid the breaking change. As you can see from the baselines, this error is pretty common. From my first run of our extended test suites, it looks pretty common in real code as well.

Note that the compiler can only check constructors for initialisation when strictNullChecks: true. I chose to be conservative and always issue the error for strictNullChecks: false.

Other ways to fix the error

  1. Make the overriding property abstract.
  2. Make the base property abstract.
  3. Give the overriding property an initialiser.
  4. Initialise the overriding property in the constructor -- but this only works with "strictNullChecks": true.

Notes

  1. Adding a postfix-! will not fix the error; ! is always erased, so, in the last example, p!: 256 | 1000 would still result in p === undefined for [[Define]] semantics.
  2. You can add declare to any uninitialised property, even if it's not an override. I previously had a check that prevented this, but I think an easy upgrade is valuable (you can almost write a regex for it).

To test this PR

{ "devDependencies": { "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/44156/artifacts?artifactName=tgz&fileId=1E6A72F0C6E099FF6B046F9DBFDBA5D1CF45DD963781CEFBCFDBBDE15FD60F7C02&fileName=/typescript-3.7.0-insiders.20190916.tgz" } }

Future work

The upcoming flag to change class field emit to [[Define]] semantics should disable this error.