From 1f3e00598360194d4fb5c64998acd68ec0c147fd Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 25 Jun 2021 13:45:03 -0400 Subject: [PATCH 1/2] update classes caveat documentation with more info on # private fields --- .../copy/en/handbook-v2/Classes.md | 80 +++++++++++++++---- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/packages/documentation/copy/en/handbook-v2/Classes.md b/packages/documentation/copy/en/handbook-v2/Classes.md index 3b06a145eee3..f2e043f2c4b7 100644 --- a/packages/documentation/copy/en/handbook-v2/Classes.md +++ b/packages/documentation/copy/en/handbook-v2/Classes.md @@ -256,24 +256,24 @@ Since [TypeScript 4.3](https://devblogs.microsoft.com/typescript/announcing-type ```ts twoslash class Thing { - _size = 0; + _size = 0; - get size(): number { - return this._size; - } - - set size(value: string | number | boolean) { - let num = Number(value); + get size(): number { + return this._size; + } - // Don't allow NaN, Infinity, etc + set size(value: string | number | boolean) { + let num = Number(value); - if (!Number.isFinite(num)) { - this._size = 0; - return; - } + // Don't allow NaN, Infinity, etc - this._size = num; + if (!Number.isFinite(num)) { + this._size = 0; + return; } + + this._size = num; + } } ``` @@ -725,7 +725,8 @@ class A { #### Caveats -Like other aspects of TypeScript's type system, `private` and `protected` are only enforced during type checking. +Like other aspects of TypeScript's type system, `private` and `protected` [are only enforced during type checking](https://www.typescriptlang.org/play?removeComments=true&target=99&ts=4.3.4#code/PTAEGMBsEMGddAEQPYHNQBMCmVoCcsEAHPASwDdoAXLUAM1K0gwQFdZSA7dAKWkoDK4MkSoByBAGJQJLAwAeAWABQIUH0HDSoiTLKUaoUggAW+DHorUsAOlABJcQlhUy4KpACeoLJzrI8cCwMGxU1ABVPIiwhESpMZEJQTmR4lxFQaQxWMm4IZABbIlIYKlJkTlDlXHgkNFAAbxVQTIAjfABrAEEC5FZOeIBeUAAGAG5mmSw8WAroSFIqb2GAIjMiIk8VieVJ8Ar01ncAgAoASkaAXxVr3dUwGoQAYWpMHBgCYn1rekZmNg4eUi0Vi2icoBWJCsNBWoA6WE8AHcAiEwmBgTEtDovtDaMZQLM6PEoQZbA5wSk0q5SO4vD4-AEghZoJwLGYEIRwNBoqAzFRwCZCFUIlFMXECdSiAhId8YZgclx0PsiiVqOVOAAaUAFLAsxWgKiC35MFigfC0FKgSAVVDTSyk+W5dB4fplHVVR6gF7xJrKFotEk-HXIRE9PoDUDDcaTAPTWaceaLZYQlmoPBbHYx-KcQ7HPDnK43FQqfY5+IMDDISPJLCIuqoc47UsuUCofAME3Vzi1r3URvF5QV5A2STtPDdXqunZDgDaYlHnTDrrEAF0dm28B3mDZg6HJwN1+2-hg57ulwNV2NQGoZbjYfNrYiENBwEFaojFiZQK08C-4fFKTVCozWfTgfFgLkeT5AUqiAA). + This means that JavaScript runtime constructs like `in` or simple property lookup can still access a `private` or `protected` member: ```ts twoslash @@ -741,7 +742,56 @@ const s = new MySafe(); console.log(s.secretKey); ``` -If you need to protect values in your class from malicious actors, you should use mechanisms that offer hard runtime privacy, such as closures, weak maps, or [private fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields). +`private` also allows access using bracket notation during type checking. This makes `private`-declared fields potentially easier to access for things like unit tests, with the drawback that these fields are _soft private_ and don't strictly enforce privacy. + +```ts twoslash +class MySafe { + private secretKey = 12345; +} + +const s = new MySafe(); + +console.log(s.secretKey); // not allowed during type checking +console.log(s["secretKey"]); // this is fine +``` + +Unlike TypeScripts's `private`, JavaScript's [private fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields) (`#`) remain private after compilation and do not provide the previously mentioned escape hatches like bracket notation access, making them _hard private_. + +```ts twoslash +class Dog { + #barkAmount = 0; + personality = "happy"; + + constructor() {} +} +``` + +```js +// After compilation... +class Dog { + constructor() { + this.#barkAmount = 0; + this.personality = "happy"; + } + #barkAmount; +} +``` + +When compiling to ES2021 or less, TypeScript will use WeakMaps in place of `#`. + +```js +// After compilation to <=ES2021 +var _Dog_barkAmount; +class Dog { + constructor() { + _Dog_barkAmount.set(this, 0); + this.personality = "happy"; + } +} +_Dog_barkAmount = new WeakMap(); +``` + +If you need to protect values in your class from malicious actors, you should use mechanisms that offer hard runtime privacy, such as closures, WeakMaps, or private fields. Note that these added privacy checks during runtime could affect performance. ## Static Members From 3ccbde7cba9e39a551118bc2c9e852e677aea67e Mon Sep 17 00:00:00 2001 From: Orta Date: Fri, 6 Aug 2021 08:10:34 +0100 Subject: [PATCH 2/2] Get code samples green, and rely on twoslash for JS emit --- .../copy/en/handbook-v2/Classes.md | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/documentation/copy/en/handbook-v2/Classes.md b/packages/documentation/copy/en/handbook-v2/Classes.md index f2e043f2c4b7..be5b9e9e2804 100644 --- a/packages/documentation/copy/en/handbook-v2/Classes.md +++ b/packages/documentation/copy/en/handbook-v2/Classes.md @@ -745,14 +745,18 @@ console.log(s.secretKey); `private` also allows access using bracket notation during type checking. This makes `private`-declared fields potentially easier to access for things like unit tests, with the drawback that these fields are _soft private_ and don't strictly enforce privacy. ```ts twoslash +// @errors: 2341 class MySafe { private secretKey = 12345; } const s = new MySafe(); -console.log(s.secretKey); // not allowed during type checking -console.log(s["secretKey"]); // this is fine +// Not allowed during type checking +console.log(s.secretKey); + +// OK +console.log(s["secretKey"]); ``` Unlike TypeScripts's `private`, JavaScript's [private fields](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields) (`#`) remain private after compilation and do not provide the previously mentioned escape hatches like bracket notation access, making them _hard private_. @@ -766,29 +770,28 @@ class Dog { } ``` -```js -// After compilation... +```ts twoslash +// @target: esnext +// @showEmit class Dog { - constructor() { - this.#barkAmount = 0; - this.personality = "happy"; - } - #barkAmount; + #barkAmount = 0; + personality = "happy"; + + constructor() {} } ``` When compiling to ES2021 or less, TypeScript will use WeakMaps in place of `#`. -```js -// After compilation to <=ES2021 -var _Dog_barkAmount; +```ts twoslash +// @target: es2015 +// @showEmit class Dog { - constructor() { - _Dog_barkAmount.set(this, 0); - this.personality = "happy"; - } + #barkAmount = 0; + personality = "happy"; + + constructor() {} } -_Dog_barkAmount = new WeakMap(); ``` If you need to protect values in your class from malicious actors, you should use mechanisms that offer hard runtime privacy, such as closures, WeakMaps, or private fields. Note that these added privacy checks during runtime could affect performance.