patterntypescriptCritical
What are the differences between the private keyword and private fields in TypeScript?
Viewed 0 times
privatetypescriptareandbetweenthedifferenceswhatfieldskeyword
Problem
In TypeScript 3.8+, what are the differences between using the
And using the
Should I prefer one over the other?
private keyword to mark a member private:class PrivateKeywordClass {
private value = 1;
}And using the
# private fields proposed for JavaScript:class PrivateFieldClass {
#value = 1;
}Should I prefer one over the other?
Solution
Private keyword
The private keyword in TypeScript is a compile time annotation. It tells the compiler that a property should only be accessible inside that class:
However compile time checking can be easily bypassed, for example by casting away the type information:
The
Emitted JavaScript
When compiling TypeScript to JavaScript, the
Becomes:
From this, you can see why the
Private fields
Private fields ensure that properties are kept private at runtime:
TypeScript will also output a compile time error if you try using a private field outside of a class:
Private fields originates from a TC-39 ECMAScript proposal and are part of the 2021 ECMAScript specification, which means that they can be used in both normal JavaScript and TypeScript.
Emitted JavaScript
If you use private fields in TypeScript and are targeting ES2021 or older versions of JavaScript for your output, TypeScript will generate code that emulates the runtime behavior of private fields using
If you are targeting anything later than ES2021, TypeScript will emit the private field:
Which one should I use?
It depends on what you are trying to achieve.
The
However if you need to enforce runtime privateness or are outputting
Also keep in mind that organization/community conventions on using one or the other will also evolve as private fields become more widespread within the JavaScript/TypeScript ecosystems.
Other differences of note
-
Private fields are not returned by
-
Private fields are not serialized by
-
There are importance edge cases around inheritance
TypeScript for example forbids declaring a private property in a subclass with the same name as a private property in the superclass.
This is not true with private fields:
-
A
Compiles to:
Whereas private fields always generate a property declaration:
Compiles to (when targetting
Further reading:
The private keyword in TypeScript is a compile time annotation. It tells the compiler that a property should only be accessible inside that class:
class PrivateKeywordClass {
private value = 1;
}
const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.However compile time checking can be easily bypassed, for example by casting away the type information:
const obj = new PrivateKeywordClass();
(obj as any).value // no compile errorThe
private keyword is also not enforced at runtime.Emitted JavaScript
When compiling TypeScript to JavaScript, the
private keyword is simply removed:class PrivateKeywordClass {
private value = 1;
}Becomes:
class PrivateKeywordClass {
constructor() {
this.value = 1;
}
}
From this, you can see why the
private keyword does not offer any runtime protection: in the generated JavaScript it's just a normal JavaScript property.Private fields
Private fields ensure that properties are kept private at runtime:
class PrivateFieldClass {
#value = 1;
getValue() { return this.#value; }
}
const obj = new PrivateFieldClass();
// You can't access '#value' outside of class like this
obj.value === undefined // This is not the field you are looking for.
obj.getValue() === 1 // But the class itself can access the private field!
// Meanwhile, using a private field outside a class is a runtime syntax error:
obj.#value
// While trying to access the private fields of another class is
// a runtime type error:
class Other {
#value;
getValue(obj) {
return obj.#value // TypeError: Read of private field #value from an object which did not contain the field
}
}
new Other().getValue(new PrivateKeywordClass());
TypeScript will also output a compile time error if you try using a private field outside of a class:
Private fields originates from a TC-39 ECMAScript proposal and are part of the 2021 ECMAScript specification, which means that they can be used in both normal JavaScript and TypeScript.
Emitted JavaScript
If you use private fields in TypeScript and are targeting ES2021 or older versions of JavaScript for your output, TypeScript will generate code that emulates the runtime behavior of private fields using
WeakMap (source).class PrivateFieldClass {
constructor() {
_x.set(this, 1);
}
}
_x = new WeakMap();
If you are targeting anything later than ES2021, TypeScript will emit the private field:
class PrivateFieldClass {
constructor() {
this.#x = 1;
}
#x;
}
Which one should I use?
It depends on what you are trying to achieve.
The
private keyword is a fine default. It accomplishes what it was designed to accomplish and has been used successfully by TypeScript developers for years. And if you have an existing codebase, you do not need to switch all of your code to use private fields. This is especially true if you are not targeting esnext, as the JS that TS emits for private fields may have a performance impact. Also keep in mind that private fields have other subtle but important differences from the private keyword .However if you need to enforce runtime privateness or are outputting
esnext JavaScript, then you should use private fields.Also keep in mind that organization/community conventions on using one or the other will also evolve as private fields become more widespread within the JavaScript/TypeScript ecosystems.
Other differences of note
-
Private fields are not returned by
Object.getOwnPropertyNames and similar methods-
Private fields are not serialized by
JSON.stringify-
There are importance edge cases around inheritance
TypeScript for example forbids declaring a private property in a subclass with the same name as a private property in the superclass.
class Base {
private value = 1;
}
class Sub extends Base {
private value = 2; // Compile error:
}This is not true with private fields:
class Base {
#value = 1;
}
class Sub extends Base {
#value = 2; // Not an error
}-
A
private keyword private property without an initializer will not generate a property declaration in the emitted JavaScript:class PrivateKeywordClass {
private value?: string;
getValue() { return this.value; }
}Compiles to:
class PrivateKeywordClass {
getValue() { return this.value; }
}
Whereas private fields always generate a property declaration:
class PrivateKeywordClass {
#value?: string;
getValue() { return this.#value; }
}Compiles to (when targetting
esnext):class PrivateKeywordClass {
#value;
getValue() { return this.#value; }
}
Further reading:
- The future of the "private" keyword
- TypeScript PR that added private fields
Code Snippets
class PrivateKeywordClass {
private value = 1;
}
const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.const obj = new PrivateKeywordClass();
(obj as any).value // no compile errorclass PrivateKeywordClass {
private value = 1;
}class Base {
private value = 1;
}
class Sub extends Base {
private value = 2; // Compile error:
}class Base {
#value = 1;
}
class Sub extends Base {
#value = 2; // Not an error
}Context
Stack Overflow Q#59641564, score: 244
Revisions (0)
No revisions yet.