To prevent the emission of a public class field to JavaScript, it is recommended to use the declare
modifier on the class field. TypeScript's implementation of class field declarations differed from JavaScript's, where an uninitialized field defaults to undefined
. To inherit a property but narrow its type in a subclass, the declare
modifier should be used.
For instance, consider a base Tree
class with a property value
of type the unknown
type and an array children
containing Tree
elements:
// TypeScript input
class Tree {
value: unknown;
children: Tree[] = [];
constructor(value: unknown) {
this.value = value;
}
}
Depending on the target and TS config settings, the emitted output may remove only the types:
// JavaScript output
class Tree {
value;
children = [];
constructor(value) {
this.value = value;
}
}
Now, creating a StringTree
subclass where value
is restricted to string
and children
is an array of StringTree
elements was best achieved by re-declaring fields in TypeScript:
// TypeScript input
class StringTree extends Tree {
value!: string;
children!: StringTree[];
constructor(value: string) {
super(value);
}
}
However, this approach may not yield the desired output:
const b = new StringTree("hello");
console.log(b.value.toUpperCase()) // RUNTIME ERROR, b.value is undefined
The subclass properties are initialized to undefined
after super()
call, leading to errors. In such cases, the error can be addressed using the declare
modifier instead:
// TypeScript input
class StringTree extends Tree {
declare value: string;
declare children: StringTree[];
constructor(value: string) {
super(value);
}
}
Subsequent output will work as intended:
// JavaScript output
class StringTree extends Tree {
constructor(value) {
super(value);
}
}
Resulting in the desired behavior:
const a = new StringTree("hello");
console.log(a.value.toUpperCase()) // "HELLO"
Playground link to code