# You Might Not Need an Enums <br /> in Typescript! @jacklee814 --- ## Agenda. - Features - Problems - Alternatives - Conclusion - Q&A - Reference --- ### Features ###### Enums allow a developer to define a set of named constants. <br />(create both a type and a value) - Numeric base - String base (v2.4+) - Heterogeneous enums - Computed and constant members - Reverse mappings - ...more ---- ### Numeric base ###### (auto-incremented) ```typescript enum Direction { Up, // 0 Down, // 1 Left, // 2 Right, // 3 } enum Direction2 { Up, // 0 Down, // 1 Left = 66, // 66 Right, // 67 } ``` ---- ### String base (v2.4+) ###### Allow you to give a meaningful and readable value. ```typescript enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT", } enum Direction2 { Up = "UP", Down = "DOWN", Left = "LEFT", // 🚨 ERROR: Enum member must have initializer. Right, } ``` ---- ### Heterogeneous enums ###### (it’s not clear way to do 🤨) ```typescript enum BooleanLikeHeterogeneousEnum { No = 0, Yes = "YES", } ``` ---- ### Computed and constant members ###### binary operators with constant enum expressions as operands <br /> ( +, -, *, /, %, <<, >>, >>>, &, |, ^ ) ```typescript enum ComputedValues { None = 0, Friendly = 1 << 0, // 0001 -> 1 Mean = 1 << 1, // 0010 -> 2 Funny = 1 << 2, // 0100 -> 4 Boring = 1 << 3, // 1000 -> 8 All = ~(~0 << 4), // 1111 -> 15 FunnyOrBoring = Funny | Boring, // 12 } ``` ---- ### Reverse mappings ```typescript enum Enum { A, } let a = Enum.A; let nameOfA = Enum[a]; // "A" ``` --- ### Problems - Runtime cost - Type safety - Limitation ---- ### Runtime cost ##### Declare without const ```typescript enum Direction { Up, Down, Left, Right, } ``` ###### emits code in the JavaScript output. 😨 ```typescript var Direction; (function (Direction) { Direction[Direction["Up"] = 0] = "Up"; Direction[Direction["Down"] = 1] = "Down"; Direction[Direction["Left"] = 2] = "Left"; Direction[Direction["Right"] = 3] = "Right"; })(Direction || (Direction = {})); ``` ---- ### Runtime cost ###### Fixed by const declaration. ```typescript const enum Direction { Up, Down, Left, Right, } ``` ###### emits code in the JavaScript output. 🙂 ```typescript= // ``` ###### But can't using Reverse mappings anymore. 😰 ```typescript const enum Direction { Up, } // 🚨 ERROR: A const enum member can only be accessed using a string literal. let nameOfUp = Direction[Direction.Up] ``` ---- ### Type safety ##### When to use Numeric enums. ```typescript enum Color { Red, Blue, } // ✅ It's fine 🤔 const Up: Color = 666; ``` ---- ### Type safety ###### Fixed by String enums. ```typescript enum Color { Red = 'Red', Blue = 'Blue', } // 🚨 ERROR: Type '666' is not assignable to type 'Color'. const primaryColor: Color = 666; ``` ---- ### Limitation - Parameters - Compatibility ---- ### Parameters ( Only accept Enum types. ) ```typescript enum Color { Red = 'Red', Blue = 'Blue', } // 🚨 ERROR: Type '"Red"' is not assignable to type 'Color'. const primaryColor: Color = 'Red'; // ✅ It's fine, But What if this is a share library 🙃? const secondaryColor: Color = Color.Blue ``` ---- ### Compatibility ##### They are't great fit for in JS/TS codebase. ###### [JavaScript enums are in the proposal stage.](https://github.com/Jack-Works/proposal-enum) --- ### Alternatives - Union type - Const assertions (v3.4+) ---- ### Union type ```typescript type Color = 'red' | 'blue'; // ✅ Type safety const rightColor: Color = 'red'; // 🚨 ERROR: Type '"green"' is not assignable to type 'Color'. const wrongColor: Color = 'green'; ``` ---- ### Const assertions (v3.4+) ###### Its syntax is a type assertion with const in place of the type name. ```typescript // Type '"hello"' let x = "hello" as const; // Object literals get readonly properties // Type 'readonly [10, 20]' let y = [10, 20] as const; // Array literals become readonly tuples // Type '{ readonly text: "hello" }' let z = { text: "hello" } as const; ``` ---- #### Type with Array and Object ```typescript const animals = ['cat', 'dog', 'mouse'] as const // type Animal = "cat" | "dog" | "mouse" type Animal = typeof animals[number] const userStatus = { REGISTERED: 'REGISTERED', INACTIVE: 'INACTIVE', NOT_FOUND: 'NOT_FOUND', } as const; type TypeUserStatus = typeof userStatus; // type UserStatus = "REGISTERED" | "INACTIVE" | "NOT_FOUND" type UserStatus = TypeUserStatus[keyof TypeUserStatus]; ``` ---- #### Usage ```typescript const userStatus = { REGISTERED: 'REGISTERED', INACTIVE: 'INACTIVE', } as const; type TypeUserStatus = typeof userStatus; type UserStatus = TypeUserStatus[keyof TypeUserStatus]; function showUserStatus(status: UserStatus) {} showUserStatus(userStatus.REGISTERED); showUserStatus('REGISTERED'); // 🚨 ERROR: Argument of type '"BANNED"' is not assignable to parameter of type 'UserStatus'. showUserStatus('BANNED'); ``` ---- #### What We Got 😏 - Type safety - More flexible - Better compatibility - Reduce code size --- ### Conclusion > You may not need an enum when an object with as const could suffice. ###### *Typescript official documentation* --- ### Q&A --- ### Reference - [TypeScript Handbook - Enums](https://www.typescriptlang.org/docs/handbook/enums.html) - [TypeScript Handbook - const assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) - [Tidy TypeScript: Prefer union types over enums](https://fettblog.eu/tidy-typescript-avoid-enums/) - [TypeScript Features to Avoid](https://www.executeprogram.com/blog/typescript-features-to-avoid)
{"metaMigratedAt":"2023-06-17T07:04:03.214Z","metaMigratedFrom":"YAML","title":"You Might Not Need an Enums in Typescript!","breaks":true,"description":"You Might Not Need an Enums in Typescript!","contributors":"[{\"id\":\"fcebd72c-ae71-48a5-9d41-996b5693508b\",\"add\":10261,\"del\":4356}]"}
    794 views