TypeScript 3.8 brings one of the most awaited features from the ECMAScript Proposal stage 3: Hash-names for private fields. Unlike the private keyword in TypeScript which is erased at compile time, hard private fields (#field) are enforced by the JavaScript runtime (V8), offering true encapsulation.
Hard Private Fields (#) vs ‘private’
class Person {
private name: string; // TypeScript "soft" private
#age: number; // ECMAScript "hard" private
constructor(name: string, age: number) {
this.name = name;
this.#age = age;
}
greet() {
console.log(`I am ${this.name}, ${this.#age} years old.`);
}
}
const p = new Person("Alice", 30);
// console.log(p.name); // ❌ Error: Property 'name' is private (TS check only)
// console.log((p as any).name); // ✅ Works at runtime!
// console.log(p.#age); // ❌ Syntax Error: Private identifier is not accessible
// console.log((p as any).#age); // ❌ Runtime Syntax Error! True privacy.
Top-Level Await
You can now use await at the top level of a module. This is huge for initializing resources like database connections before the module exports anything.
// db.ts
import { connect } from "pg";
const client = connect("postgres://...");
// The module execution pauses here until connected!
await client.connect();
export default client;
Type-Only Imports
To help bundlers like Webpack perform tree-shaking, we can now explicit import types.
import type { UserConfig } from "./types";
import { createConfig } from "./utils";
// UserConfig is completely erased from the JS output
const config: UserConfig = createConfig();
Key Takeaways
- Use
#fieldfor runtime privacy, particularly in library code. - Use
import typeto improve build performance and tree-shaking. - Top-level await simplifies module initialization logic, removing the need for
(async () => { ... })()wrappers.
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.