From 2c186310d6741182534ecb9bea0b595817a464ad Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 25 Apr 2025 17:19:39 -0400 Subject: [PATCH 1/5] Initial pass of documentation on enums. --- docs/architecture/clients/enums.md | 101 +++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 docs/architecture/clients/enums.md diff --git a/docs/architecture/clients/enums.md b/docs/architecture/clients/enums.md new file mode 100644 index 000000000..496837b2e --- /dev/null +++ b/docs/architecture/clients/enums.md @@ -0,0 +1,101 @@ +# Why Bitwarden Is Moving Away from TypeScript `enum`s + +Following an architecture discussion we've decided to stop using TypeScript `enum`s in favor of a +safer and more maintainable alternative. + +### Reasons include: + +- ✅ Type-safe +- ✅ Smaller bundle size +- ✅ Flexible and extendable +- ✅ Fewer surprises + +--- + +## Our Recommended Approach + +Instead of using `enum`s, we use constant objects and derive union types: + +**Safe Alternative:** + +```ts +export const CipherTypes = { + Login: 1, + SecureNote: 2, + Card: 3, + Identity: 4, + SshKey: 5, +} as const; + +export type CipherTypeValue = (typeof CipherTypes)[keyof typeof CipherTypes]; + +declare function useCipher(type: CipherTypeValue): void; + +useCipher(CipherTypes.Login); // ✅ Valid +useCipher(42); // ❌ Invalid +``` + +--- + +## The Problems with `enum`s + +### 1. `enum`s Emit Extra Code + +TypeScript `enum`s generate additional JavaScript code at compile time, increasing bundle size and +potentially degrading performance. + +**Example:** + +```ts +export enum CipherType { + Login = 1, + SecureNote = 2, + Card = 3, + Identity = 4, + SshKey = 5, +} +``` + +**Compiled Output:** + +```js +var CipherType; +(function (CipherType) { + CipherType[(CipherType["Login"] = 1)] = "Login"; + CipherType[(CipherType["SecureNote"] = 2)] = "SecureNote"; + CipherType[(CipherType["Card"] = 3)] = "Card"; + CipherType[(CipherType["Identity"] = 4)] = "Identity"; + CipherType[(CipherType["SshKey"] = 5)] = "SshKey"; +})(CipherType || (CipherType = {})); +``` + +--- + +### 2. Numeric `enum`s Are Not Type Safe + +TypeScript allows arbitrary numbers to be passed to functions expecting a numeric `enum`. + +**Example:** + +```ts +declare function useCipher(type: CipherType): void; + +useCipher(42); // This compiles! 😱 +``` + +This undermines the purpose of type safety and can introduce hard-to-track bugs. + +--- + +### 3. `enum`s Are Named Types + +Even when using string or numeric enums, their named type behavior reduces compatibility with +structurally similar values. + +--- + +## Resources Used + +https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh +https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums +https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-optio From 2abee6f069ad91abae8f5dc118584929ba3675df Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Tue, 29 Apr 2025 10:09:14 -0400 Subject: [PATCH 2/5] Update enums.md --- docs/architecture/clients/enums.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/architecture/clients/enums.md b/docs/architecture/clients/enums.md index 496837b2e..9c2fa05a8 100644 --- a/docs/architecture/clients/enums.md +++ b/docs/architecture/clients/enums.md @@ -59,14 +59,14 @@ export enum CipherType { **Compiled Output:** ```js -var CipherType; -(function (CipherType) { - CipherType[(CipherType["Login"] = 1)] = "Login"; - CipherType[(CipherType["SecureNote"] = 2)] = "SecureNote"; - CipherType[(CipherType["Card"] = 3)] = "Card"; - CipherType[(CipherType["Identity"] = 4)] = "Identity"; - CipherType[(CipherType["SshKey"] = 5)] = "SshKey"; -})(CipherType || (CipherType = {})); +export let CipherType +;(function(CipherType) { + CipherType[(CipherType["Login"] = 1)] = "Login" + CipherType[(CipherType["SecureNote"] = 2)] = "SecureNote" + CipherType[(CipherType["Card"] = 3)] = "Card" + CipherType[(CipherType["Identity"] = 4)] = "Identity" + CipherType[(CipherType["SshKey"] = 5)] = "SshKey" +})(CipherType || (CipherType = {})) ``` --- From cb34bae23a480196deda0d66af252938fe232f97 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 1 May 2025 16:03:01 -0400 Subject: [PATCH 3/5] Updated from comments. --- .../code-style}/enums.md | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) rename docs/{architecture/clients => contributing/code-style}/enums.md (62%) diff --git a/docs/architecture/clients/enums.md b/docs/contributing/code-style/enums.md similarity index 62% rename from docs/architecture/clients/enums.md rename to docs/contributing/code-style/enums.md index 9c2fa05a8..615a0e94f 100644 --- a/docs/architecture/clients/enums.md +++ b/docs/contributing/code-style/enums.md @@ -1,16 +1,14 @@ -# Why Bitwarden Is Moving Away from TypeScript `enum`s +# How to use Enums -Following an architecture discussion we've decided to stop using TypeScript `enum`s in favor of a -safer and more maintainable alternative. +Following an architecture discussion we've decided to stop using TypeScript's native `enum`s and +favor using objects. ### Reasons include: -- ✅ Type-safe -- ✅ Smaller bundle size -- ✅ Flexible and extendable -- ✅ Fewer surprises - ---- +- Type-safe +- Smaller bundle size +- Feature parity with native TypeScript enums. +- Fewer surprises ## Our Recommended Approach @@ -35,8 +33,6 @@ useCipher(CipherTypes.Login); // ✅ Valid useCipher(42); // ❌ Invalid ``` ---- - ## The Problems with `enum`s ### 1. `enum`s Emit Extra Code @@ -59,18 +55,16 @@ export enum CipherType { **Compiled Output:** ```js -export let CipherType -;(function(CipherType) { - CipherType[(CipherType["Login"] = 1)] = "Login" - CipherType[(CipherType["SecureNote"] = 2)] = "SecureNote" - CipherType[(CipherType["Card"] = 3)] = "Card" - CipherType[(CipherType["Identity"] = 4)] = "Identity" - CipherType[(CipherType["SshKey"] = 5)] = "SshKey" -})(CipherType || (CipherType = {})) +var CipherType; +(function (CipherType) { + CipherType[(CipherType["Login"] = 1)] = "Login"; + CipherType[(CipherType["SecureNote"] = 2)] = "SecureNote"; + CipherType[(CipherType["Card"] = 3)] = "Card"; + CipherType[(CipherType["Identity"] = 4)] = "Identity"; + CipherType[(CipherType["SshKey"] = 5)] = "SshKey"; +})(CipherType || (CipherType = {})); ``` ---- - ### 2. Numeric `enum`s Are Not Type Safe TypeScript allows arbitrary numbers to be passed to functions expecting a numeric `enum`. @@ -85,17 +79,13 @@ useCipher(42); // This compiles! 😱 This undermines the purpose of type safety and can introduce hard-to-track bugs. ---- - ### 3. `enum`s Are Named Types Even when using string or numeric enums, their named type behavior reduces compatibility with structurally similar values. ---- - ## Resources Used -https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh -https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums -https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-optio +- https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh +- https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums +- https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-optio From 7472080261b899015c95429766985ef6119f1efa Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 5 May 2025 10:47:40 -0400 Subject: [PATCH 4/5] Updated from comments. --- docs/contributing/code-style/enums.md | 68 ++------------------------- 1 file changed, 3 insertions(+), 65 deletions(-) diff --git a/docs/contributing/code-style/enums.md b/docs/contributing/code-style/enums.md index 615a0e94f..f1b9c8f6e 100644 --- a/docs/contributing/code-style/enums.md +++ b/docs/contributing/code-style/enums.md @@ -1,21 +1,10 @@ -# How to use Enums +# TypeScript Const Enums -Following an architecture discussion we've decided to stop using TypeScript's native `enum`s and -favor using objects. - -### Reasons include: - -- Type-safe -- Smaller bundle size -- Feature parity with native TypeScript enums. -- Fewer surprises +We don't use Typescript enums because they are not fully type-safe and can cause surprises. Instead +of using enums, we use constant objects. ## Our Recommended Approach -Instead of using `enum`s, we use constant objects and derive union types: - -**Safe Alternative:** - ```ts export const CipherTypes = { Login: 1, @@ -33,57 +22,6 @@ useCipher(CipherTypes.Login); // ✅ Valid useCipher(42); // ❌ Invalid ``` -## The Problems with `enum`s - -### 1. `enum`s Emit Extra Code - -TypeScript `enum`s generate additional JavaScript code at compile time, increasing bundle size and -potentially degrading performance. - -**Example:** - -```ts -export enum CipherType { - Login = 1, - SecureNote = 2, - Card = 3, - Identity = 4, - SshKey = 5, -} -``` - -**Compiled Output:** - -```js -var CipherType; -(function (CipherType) { - CipherType[(CipherType["Login"] = 1)] = "Login"; - CipherType[(CipherType["SecureNote"] = 2)] = "SecureNote"; - CipherType[(CipherType["Card"] = 3)] = "Card"; - CipherType[(CipherType["Identity"] = 4)] = "Identity"; - CipherType[(CipherType["SshKey"] = 5)] = "SshKey"; -})(CipherType || (CipherType = {})); -``` - -### 2. Numeric `enum`s Are Not Type Safe - -TypeScript allows arbitrary numbers to be passed to functions expecting a numeric `enum`. - -**Example:** - -```ts -declare function useCipher(type: CipherType): void; - -useCipher(42); // This compiles! 😱 -``` - -This undermines the purpose of type safety and can introduce hard-to-track bugs. - -### 3. `enum`s Are Named Types - -Even when using string or numeric enums, their named type behavior reduces compatibility with -structurally similar values. - ## Resources Used - https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh From 69a927a01fdb962d97b37a8eb40a0b16f1673c34 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 9 May 2025 15:00:50 -0400 Subject: [PATCH 5/5] Added in jon's comments --- docs/contributing/code-style/enums.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/docs/contributing/code-style/enums.md b/docs/contributing/code-style/enums.md index f1b9c8f6e..5ed0044a5 100644 --- a/docs/contributing/code-style/enums.md +++ b/docs/contributing/code-style/enums.md @@ -6,24 +6,20 @@ of using enums, we use constant objects. ## Our Recommended Approach ```ts -export const CipherTypes = { - Login: 1, - SecureNote: 2, - Card: 3, - Identity: 4, - SshKey: 5, +export const PasskeyActions = { + Register: "register", + Authenticate: "authenticate", } as const; -export type CipherTypeValue = (typeof CipherTypes)[keyof typeof CipherTypes]; +export type PasskeyActionValue = (typeof PasskeyActions)[keyof typeof PasskeyActions]; -declare function useCipher(type: CipherTypeValue): void; +declare function usePasskeyAction(action: PasskeyActionValue): void; -useCipher(CipherTypes.Login); // ✅ Valid -useCipher(42); // ❌ Invalid +usePasskey(PasskeyActions.Register); // ✅ Valid +usePasskey(0); // ❌ Invalid ``` ## Resources Used - https://dev.to/ivanzm123/dont-use-enums-in-typescript-they-are-very-dangerous-57bh - https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums -- https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-optio