Skip to content

Commit

Permalink
XS✔ ◾ Typescript Enums Rule (#8193)
Browse files Browse the repository at this point in the history
* added base rule

* added bits to include object const assertions

* Added image of vscode use

* added vscode screenshots for object const assertions

* bit of content cleanup

* added intro sentence

* added correct timestamp

* Auto-fix Markdown files

* Update rules/typescript-enums/rule.md

Co-authored-by: Brady Stroud [SSW] <[email protected]>

* Auto-fix Markdown files

* Update rules/typescript-enums/rule.md

Co-authored-by: Brady Stroud [SSW] <[email protected]>

* Update rules/typescript-enums/rule.md

Co-authored-by: Brady Stroud [SSW] <[email protected]>

* Update rules/typescript-enums/rule.md

Co-authored-by: Brady Stroud [SSW] <[email protected]>

* Update rules/typescript-enums/rule.md

Co-authored-by: Brady Stroud [SSW] <[email protected]>

* Auto-fix Markdown files

* Update rules/typescript-enums/rule.md

Co-authored-by: Jeoffrey Fischer [SSW] <[email protected]>

* Auto-fix Markdown files

* made jeoffrey's requested changes

* requested changes from jeoffrey

* Auto-fix Markdown files

* changed title to be more readable

* Update rules/typescript-enums/rule.md

Formatted enum

Co-authored-by: Matt Wicks [SSW] <[email protected]>

* Update rules/typescript-enums/rule.md

Reworded phrasing

Co-authored-by: Matt Wicks [SSW] <[email protected]>

* added point about consts

* added headings + good and bad examples

* Auto-fix Markdown files

* Added sentence at bottom for usage of when an enum is preferred

* added single source of truth part to sentence

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tiago Araújo [SSW] <[email protected]>
Co-authored-by: Brady Stroud [SSW] <[email protected]>
Co-authored-by: Jeoffrey Fischer [SSW] <[email protected]>
Co-authored-by: Matt Wicks [SSW] <[email protected]>
  • Loading branch information
6 people authored Mar 26, 2024
1 parent c74ee29 commit 6ae8af9
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ uri: rules-to-better-typescript
index:
- avoid-using-any
- describe-types-sparsely
- typescript-enums
- follow-good-object-oriented-design-patterns
- good-typescript-configuration
- only-export-what-is-necessary
Expand Down
Binary file added rules/typescript-enums/icon-vscode-sense.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added rules/typescript-enums/iconkey-vscode-sense.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 127 additions & 0 deletions rules/typescript-enums/rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
---
type: rule
title: Do you know why to use const assertions instead of TypeScript enums?
uri: typescript-enums
authors:
- title: Harry Ross
url: https://ssw.com.au/people/harry-ross
related:
- use-enums-instead-of-hard-coded-strings
created: 2024-03-19T21:39:38.906Z
archivedreason: null
guid: ba19be99-354d-44b2-a2da-4131cc660f18
---
It's super important to ensure that [magic strings are not used in your codebase](https://www.ssw.com.au/rules/use-enums-instead-of-hard-coded-strings/). Typically, we would use constant values or enums to solve this problem, but this may not be applicable when using TypeScript. You might expect TypeScript enums to function like strongly typed languages like C# but often this is not the case.

<!--endintro-->

`youtube: jjMbPt_H3RQ`
**Video: Enums considered harmful (9 min)**

While TypeScript enums provide a lot of useful type safety at runtime, it's very important to consider that there may be cleaner options.

## Numerical Enums

When you define an enum like this:

```ts
enum Fruits {
Apple,
Banana,
Cherry
}
```

When compiled to JavaScript, it looks like:

```js
var Fruits;
(function (Fruits) {
Fruits[Fruits["Apple"] = 0] = "Apple";
Fruits[Fruits["Banana"] = 1] = "Banana";
Fruits[Fruits["Cherry"] = 2] = "Cherry";
})(Fruits || (Fruits = {}));
```

However, this makes it hard to loop over the keys of the enum, as when you run `Object.keys(Fruits)` you would get the following array returned:

::: bad

```ts
["0", "1", "2", "Apple", "Banana", "Cherry"]
```

**Bad Example - an irritating DX, instead of returning just the values of the enum**
:::

Instead, a much cleaner option is by using [const assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions). With const assertions we can be sure the code is using the string values we want:

```ts
const fruits = ["Apple", "Banana", "Cherry"] as const;
```

Now, if we look into the content of the shapes array using:

```ts
type Fruit = typeof fruits[number];
```

We can construct this type from the above array, which is equivalent to:

::: good

```ts
type Fruit = "Apple" | "Banana" | "Cherry";
```

**Good Example - a much cleaner DX**
:::

This makes it super easy to loop over keys within a union type. This also allows us to be able to pass `"Apple"` into a function that takes `Fruit` as an argument. We get super useful feedback from our code editor - the same as a typical TypeScript union type from VSCode from the `Fruit` union type:

![Figure: Working VSCode Intellisense that works with all const assertions](vscode-intellisense-array2.png)

## String Enums

::: bad

```tsx
enum Icon {
sun = "sun",
moon = "moon"
}

const icons: Record<Icon, string> = {
sun: "sun_12345.jpg",
moon: "moon_543212.jpg"
};
```

**Bad Example - duplication of key values where it is not needed**
:::

This is problematic, as it provides us no useful type hints for object values, as object values are typed as `string`, and there is an unecessary duplication of object keys. For cases like this with a single source of truth (i.e. the `icons` object), we can use const assertions, similiar to above with objects:

::: good

```tsx
const icons = {
sun: "sun_12345.jpg",
moon: "moon_543212.jpg",
} as const;

type IconKey = keyof typeof icons; // "sun" | "moon" union type

type Icon = (typeof icons)[IconKey]; // "sun_12345.jpg" | "moon_543212.jpg" union type
```

**Good Example - a much cleaner DX with a single source of truth in the `as const` object**
:::

Similar to the array const assertion above, these also provide useful type hints in your code editor:

![Figure: Using the Icon type from above](icon-vscode-sense.png)

![Figure: The IconKey type from above](iconkey-vscode-sense.png)

Remember, it's important to assess on a case-by-case basis when you are writing code to determine whether a const assertion can be used instead of an enum. For example, it's important when dealing with object values you don't want to overlap (i.e. `Icon1`, `Icon2` both with a `moon` key but with different moon images) to opt for enums. However, using const assertions will likely lead to better DX (Developer eXperience) in most cases.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6ae8af9

Please sign in to comment.