Rule 8.13
Chapter 8, Functions and entry points Test
A static field must be a module constant Owner_x when nothing in the program writes it, and a module variable in the per-invocation space (Rule 6.5) when something does; a readonly static is never written.
A class inherits the statics of the classes it extends: D.K, D.f() and D.x reach the nearest class above D that declares them, and a static function or accessor D inherits must be lowered again for D.
Inside a static member this is the class the call names, D in D.f(): this.K, this.f() and this.x = v name its statics, new this(...) builds it, and a static that returns the class the call names says so with a this parameter, static make<C extends B>(this: { new (): C; K: f32 }): C, which is a type and not a parameter anything passes, and returns D; super.K, super.f() and super.x name the statics of the class above the one that wrote the member.
A write to a static field through a class that does not declare it (D.K = v, this.K = v, super.K = v), this.#x where this is a class that extends the one declaring #x, this as a value, a static block, and a static declared to return the class that declares it that builds its value with new this(...) where a class inherits it must be refused with the remedy; a write into such a static (D.origin.y = v, a method that changes it) changes the one object both classes read, and a generic base’s statics are its class’s, one for every instance.
The rule text and the parts under it are the compiler's own English, as the design document writes them.
Rationale
a static field that TypeScript code writes is state of the program’s run, which on the GPU is the invocation, so it is the variable a top-level let already is (surface §24); one no code writes folds as a top-level const does; a static block runs when a class is evaluated, and a shader has no such moment. TypeScript keeps statics on the constructor, whose prototype is its base’s constructor, so a read finds the nearest declaration, this in a static member is the constructor the call names, and a write through a derived constructor gives it a property of its own, which one module variable cannot be; a private static lives on its declaring constructor alone, so TypeScript throws where a derived class reaches it through this. TypeScript types a call by the return type the member writes, so a static that returns the class the call names writes that class as its this parameter’s construct signature, and one that writes the declaring class would be that class to the editor and D here (Rule 12.7, proposal 0020).
Derives from
Rule 6.3; Rule 6.5; ECMAScript ClassDefinitionEvaluation (the constructor’s prototype is the base constructor) and the super keyword; Variable and Value Declarations; surface §24 and §26.
How it is verified
Checked by a test. A test, a gate script or a CI workflow names this rule, and the traceability check fails when a file listed below stops naming it.
Where the rule says the compiler enforces it:
TS8035 CLASS_MEMBER for a static block, for this as a value, for this.#x through a class that extends the one declaring it ("Derived" has no "#k": a private static is the class's own, and TypeScript throws where a body "Derived" inherits reaches it through "this". Name the class that declares it, "Base.#k".) and for super.K = v; TS8005 CONST_ASSIGN for a write to a readonly static and for one through a class that does not declare it ("Derived.count" is a static "Base" declares, and assigning it through "Derived" would give "Derived" a copy of its own in TypeScript. Write "Base.count".); TS8035 CLASS_MEMBER for new this() outside a static member; TS8035 CLASS_MEMBER for a static declared to return the class that declares it that builds its value with new this() where a class inherits it ("Base.make" builds its value with "new this()", so "Derived.make()" returns a Derived, but it is declared to return a Base, which is the type the editor gives the call. Declare the class the call names: static make<C extends Base>(this: { new (): C; SCALE: f32 }): C), pinned with the editor’s half by src/compiler/ts/class-syntax.test.ts; pinned by src/compiler/ts/class-syntax.test.ts and src/compiler/ts/class-methods.test.ts, which also hold the three CPU paths to one value for a static a derived class inherits and for super in a static member; examples/class-builder.shade.ts in the compile gate.
The files that verify it at commit 26de7be8, each at the first line that names the rule:
Explained in
The sections of the surface document that explain this rule, at commit 26de7be8:
Error codes that enforce it
The diagnostic codes the rule names under Enforced by, or whose registry text names the rule:
See also
Source
The rule at commit 26de7be8:
docs/language-design.md:742(the design document)reqs/rules/RULE-0813.md(its traceability item)