Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove all TypeScript enums and forbid them in ESLint #4580

Merged
merged 3 commits into from Jul 3, 2023

Conversation

aloisklink
Copy link
Member

📑 Summary

TypeScript enums are an unusual TypeScript feature, because it's one of the only features that is "not a type-level extension of JavaScript".

This means TypeScript generates custom code for enums, which can cause a bunch of issues, especially as this custom code can be built differently depending on which bundler you use, and because of this, many people discourage the use of enums:

In fact, even the official TypeScript handbook says:

Enums are a feature added to JavaScript by TypeScript which allows for describing a value which could be one of a set of possible named constants. Unlike most TypeScript features, this is not a type-level addition to JavaScript but something added to the language and runtime. Because of this, it’s a feature which you should know exists, but maybe hold off on using unless you are sure.

Taken from https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums, used under the CC BY 4.0 License.

I've also added an ESLint rule to ban TypeScript enums, using the no-restricted-syntax ESLint rule.

Better alternatives for TypeScript enums

For public APIs (e.g. that non-Mermaid users will use), it's better to use string literals, e.g.:

export type MyValue = "a" | "b" | "c";

For internal use, const assertions where added in TypeScript 3.4 and can be used to create enum-like objects in plain JavaScript, that act like TypeScript enums, but has none of the downsides of TypeScript enums.

Using a JavaScript object and as const performs pretty much exactly the same as a TypeScript enum, without the downsides of a TypeScript enum:

const MyEnum = {
  ABC: "my-string-val",
  DEF: "my-other-enum-val",
} as const;

📏 Design Decisions

Important: This is a breaking change to the MermaidConfig Sankey diagram type. However, since the Sankey diagram PR was only merged yesterday (see #4502), as long as this PR is merged before the next Mermaid release, this change will be fine.

📋 Tasks

Make sure you

  • 📖 have read the contribution guidelines
  • 💻 have added unit/e2e tests (if appropriate)
    • ESLint rule added.
  • 📓 have added documentation (if appropriate)
    • Warning added to ESLint rule.
  • 🔖 targeted develop branch

Replace the TypeScript `enum {a = "a", b = "b"}` types with
TypeScript's literal types (e.g. `"a" | "b"`).

This is because TypeScript enums are
[_not_ a type-level addition to JavaScript][1], and even the official
TypeScript docs say to be careful when using.

[1]: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums
[`const` assertions where added in TypeScript 3.4][1] and can be used
to create enum-like objects in plain JavaScript, that act like
TypeScript enums, but has none of the downsides of TypeScript enums.

[1]: https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions
[TypeScript enums][1] are an unusual TypeScript feature, because it's
one of the only features that is "not a type-level extension of
JavaScript".

This means TypeScript generates custom code for enums, which can cause a
bunch of issues, especially as this custom code can be built differently
depending on which bundler you use, and because of this, many people
discourage the use of enums:

- [The Dangers of TypeScript Enums][2]
- [Tidy TypeScript: Prefer union types over enums][3]
- [TypeScript: Handbook - Enums # Objects vs Enums][4]

I've added an ESLint rule that forbids TypeScript enums, as in most
cases, it's better to use string literals instead, e.g. like
`type a = "a" | "b" | "c";`.

[1]: https://www.typescriptlang.org/docs/handbook/enums.html#string-enums
[2]: https://dev.to/azure/the-dangers-of-typescript-enums-55pd
[3]: https://fettblog.eu/tidy-typescript-avoid-enums/
[4]: https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums
@aloisklink aloisklink mentioned this pull request Jul 2, 2023
18 tasks
@codecov
Copy link

codecov bot commented Jul 2, 2023

Codecov Report

Merging #4580 (79688a1) into develop (9c011ab) will increase coverage by 4.75%.
The diff coverage is 90.00%.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4580      +/-   ##
===========================================
+ Coverage    75.10%   79.85%   +4.75%     
===========================================
  Files          135      143       +8     
  Lines        16498    16499       +1     
  Branches       510      519       +9     
===========================================
+ Hits         12390    13176     +786     
+ Misses        3997     3218     -779     
+ Partials       111      105       -6     
Flag Coverage Δ
e2e 84.01% <66.66%> (+8.45%) ⬆️
unit 58.08% <100.00%> (-2.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...ages/mermaid/src/diagrams/sankey/sankeyRenderer.ts 85.18% <50.00%> (ø)
packages/mermaid/src/config.ts 72.61% <100.00%> (+2.21%) ⬆️
packages/mermaid/src/defaultConfig.ts 93.82% <100.00%> (ø)

... and 41 files with indirect coverage changes

@nirname
Copy link
Contributor

nirname commented Jul 3, 2023

@aloisklink Thank you for being attentive to details. As a non TS programmer I cannot fully appreciate all the nuances, but I have experience with other languages that support this.

Enums originated quite a time ago in static typed languages, and in majority of cases they are implemented as numeric constants under the hood. Like in c++:

enum Weekday {
  Monday, // 0
  Tuesday // 1
}

And such a notation it is absolutely valid (not checking syntax, though):

Weekday(0);

So the statement

enums are not a type safe

related not only to typescript, but rather to the the nature of enums. There are many cases when these could be type-casted dynamically to Int and vise versa, like in comparison operations for instance.

Speaking about string enums, there was no such a thing in older c++ versions (I am not following latest standards), so there was some tricks to do the same for strings, including but not limited to:

  • writing macros, which is in fact a sub language of c++ (thats overcomplicated sometimes), so it would be interpolated to the result code in-place, or
  • using string constants

None of the latter are memory-economic, so there was even some more evil hacks to achieve desired behavior.

I've read the articles you provided and from my background the arguments "to avoid them unless you are sure" as well as "not being type safe" are not strong enough. They behave almost exactly the same way as in other languages, and I consider it as expected behavior. Since there is a enum in typescript why not use it?

Nevertheless, I am OK "string constants" as a preferred way in that case. Perhaps, enums can confuse other developers who never faced it. Or we may run into an issue with it later on.

The main thing that can cause misunderstanding is that a user's configuration contains a JSON with string always, not a enum.

From my perspective (I hope I don't hurt anyone's feelings) the whole typescript is somewhat an imperfect patch trying to fix JavaScript "weakly" typed design (not unlike PHP) and resulting scalability problem. So it does not really matter to me which type to use. What matters is that it is better to choose one option and to stick with it for the whole project, thus your change that forbids enums is absolutely valid.

TLDR, summarize:

  • I dont have personal preferences on what to use (enums, vs constans), I am ok with both
  • Graph configuration is JSON with string always, not a enum, that can cause misunderstanding
  • If we dont use enums, then forbid it (done), or stick to them whenever it is possible

@sidharthv96 as a main reviewer of Sankey diagrams, what do you think?

@aloisklink
Copy link
Member Author

From my perspective (I hope I don't hurt anyone's feelings) the whole typescript is somewhat an imperfect patch trying to fix JavaScript "weakly" typed design

I agree. That's actually the main issue with TypeScript enums, it's a TypeScript-only feature that doesn't exist in JavaScript. Pretty much all modern TypeScript features are only type-level features. E.g., you can delete all the type information, and you'll be left with perfectly valid JavaScript code.

TypeScript enums were added to the language a long time ago, and are different because they require custom JavaScript code to be generated (there's even been talk about potentially deprecating it, although it's a bit unlikely since it will break a lot of legacy code).

There's even a proposal for enums to officially be added to JavaScript (see https://github.com/rbuckton/proposal-enum), and if that gets added, it might break existing TypeScript enum code, since they use the same syntax. (TypeScript enums are also excluded from the ECMAScript proposal: Type Annotations for this same reason).

It's generally safe to use TypeScript enum is private files, but if you're making a public API, then using a TypeScript enum usually isn't a good idea.

From a C++ perspective, imagine TypeScript enums to be a feature that isn't part of the official C++ standard, but something that's only supported in a specific C++ compiler.

What matters is that it is better to choose one option and to stick with it for the whole project

👍 That's good to hear! My vote is to remove them and forbid them, since otherwise, once we have them, we wouldn't be able to remove them without breaking backwards compatibility, but I'd love to get the comments from anybody else that has TypeScript experience.

@sidharthv96
Copy link
Member

I think the given reasons are valid, and mermaid being a library that will be consumed by other developers, sticking to the core is the best option for us.

The changes in this PR making use of constants are simple and enhances the readability.
Especially in places like
image
where the []: syntax might confuse people.

@sidharthv96 sidharthv96 added this pull request to the merge queue Jul 3, 2023
Merged via the queue into mermaid-js:develop with commit 6d7cd2b Jul 3, 2023
16 checks passed
@aloisklink aloisklink deleted the refactor/replace-enums branch July 5, 2023 18:10
github-merge-queue bot pushed a commit to fuxingloh/contented that referenced this pull request Jul 31, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [mermaid](https://togithub.com/mermaid-js/mermaid) | [`10.2.4` ->
`10.3.0`](https://renovatebot.com/diffs/npm/mermaid/10.2.4/10.3.0) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/mermaid/10.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/mermaid/10.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/mermaid/10.2.4/10.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/mermaid/10.2.4/10.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>mermaid-js/mermaid (mermaid)</summary>

###
[`v10.3.0`](https://togithub.com/mermaid-js/mermaid/releases/tag/v10.3.0):
10.3.0

[Compare
Source](https://togithub.com/mermaid-js/mermaid/compare/v10.2.4...v10.3.0)

#### What's Changed

##### Features

- Sankey diagrams by [@&#8203;nirname](https://togithub.com/nirname) in
[mermaid-js/mermaid#4502
- Feature/1838 actor creation destruction by
[@&#8203;Valentine14th](https://togithub.com/Valentine14th) in
[mermaid-js/mermaid#4466
- Vertical branches in Git Diagram by
[@&#8203;mastersibin](https://togithub.com/mastersibin) in
[mermaid-js/mermaid#4639
- Use JSON Schema to define and document `MermaidConfig` by
[@&#8203;aloisklink](https://togithub.com/aloisklink) in
[mermaid-js/mermaid#4112
- Remove the test checking whether the JSON Schema default config
matched the old default config by
[@&#8203;aloisklink](https://togithub.com/aloisklink) in
[mermaid-js/mermaid#4610
- Fixes support of the macro `ContainerQueue_Ext` for C4 diagrams
definition. by [@&#8203;kislerdm](https://togithub.com/kislerdm) in
[mermaid-js/mermaid#4577

##### Bugfixes

- Make quadrant chart options TypeScript types optional by
[@&#8203;aloisklink](https://togithub.com/aloisklink) in
[mermaid-js/mermaid#4602
- Remove double parsing by
[@&#8203;nirname](https://togithub.com/nirname) in
[mermaid-js/mermaid#4587
- Fix flowchart tooltip typing bug by
[@&#8203;lishid](https://togithub.com/lishid) in
[mermaid-js/mermaid#4562
- Bug/4590 allow notes identical to keywords by
[@&#8203;ibrahimWassouf](https://togithub.com/ibrahimWassouf) in
[mermaid-js/mermaid#4597
- feat: allow specifying on which weekday a tickInterval should start by
[@&#8203;leinelissen](https://togithub.com/leinelissen) in
[mermaid-js/mermaid#4634
- Split formatted markdown strings with unicode support. by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4470
- fix: Mind maps handles `-` signs in node ids/text by
[@&#8203;knsv](https://togithub.com/knsv)

##### Chores

- Remove all TypeScript enums and forbid them in ESLint by
[@&#8203;aloisklink](https://togithub.com/aloisklink) in
[mermaid-js/mermaid#4580
- refactor accessibility by
[@&#8203;Yokozuna59](https://togithub.com/Yokozuna59) in
[mermaid-js/mermaid#4551
- chore: Reduce codecov pushes by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4604
- Run PR-labeler-config-validator only if config changes by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4607
- chore(deps): update all minor dependencies (minor) by
[@&#8203;renovate](https://togithub.com/renovate) in
[mermaid-js/mermaid#4624
- Update all patch dependencies (patch) by
[@&#8203;renovate](https://togithub.com/renovate) in
[mermaid-js/mermaid#4566
- Update all patch dependencies (patch) by
[@&#8203;renovate](https://togithub.com/renovate) in
[mermaid-js/mermaid#4581
- Rename workflow jobs by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4574
- Removed unused code in state diagrams by
[@&#8203;nirname](https://togithub.com/nirname) in
[mermaid-js/mermaid#4631
- chore(deps): update all patch dependencies (patch) by
[@&#8203;renovate](https://togithub.com/renovate) in
[mermaid-js/mermaid#4623
- chore: remove unused `devDependency` on coveralls by
[@&#8203;aloisklink](https://togithub.com/aloisklink) in
[mermaid-js/mermaid#4641
- Allow entity diagram attribute names to start with asterisk by
[@&#8203;ibrahimWassouf](https://togithub.com/ibrahimWassouf) in
[mermaid-js/mermaid#4588
- Bug/4592 fix new line padding class diagram by
[@&#8203;ibrahimWassouf](https://togithub.com/ibrahimWassouf) in
[mermaid-js/mermaid#4633
- Fix graph not loading when the img loads too fast or fail to load by
[@&#8203;pierrickouw](https://togithub.com/pierrickouw) in
[mermaid-js/mermaid#4496
- convert `cypress/helpers/util.js` to ts by
[@&#8203;Yokozuna59](https://togithub.com/Yokozuna59) in
[mermaid-js/mermaid#4552
- build(deps-dev): bump word-wrap from 1.2.3 to 1.2.4 by
[@&#8203;dependabot](https://togithub.com/dependabot) in
[mermaid-js/mermaid#4652
- chore(deps): update all minor dependencies (minor) by
[@&#8203;renovate](https://togithub.com/renovate) in
[mermaid-js/mermaid#4663
- chore(deps): update all patch dependencies (patch) by
[@&#8203;renovate](https://togithub.com/renovate) in
[mermaid-js/mermaid#4662

##### Documentation

- Sankey: Remove duplicated examples by
[@&#8203;nirname](https://togithub.com/nirname) in
[mermaid-js/mermaid#4595
- Release docs by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4493
- Update latest news section by
[@&#8203;huynhicode](https://togithub.com/huynhicode) in
[mermaid-js/mermaid#4495
- Fix Typo by [@&#8203;ryru](https://togithub.com/ryru) in
[mermaid-js/mermaid#4567
- Docs: add ChatGPT plugin blog post by
[@&#8203;huynhicode](https://togithub.com/huynhicode) in
[mermaid-js/mermaid#4570
- Fix relative link to theme variables list by
[@&#8203;ibrahimWassouf](https://togithub.com/ibrahimWassouf) in
[mermaid-js/mermaid#4573
- Fix docs:dev by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4598
- Docs: update link - "Join the Community" by
[@&#8203;huynhicode](https://togithub.com/huynhicode) in
[mermaid-js/mermaid#4601
- Support docs:dev in docker by
[@&#8203;nirname](https://togithub.com/nirname) in
[mermaid-js/mermaid#4599
- docs(flowchart): add documentation on multiple nodes style by
[@&#8203;tomperr](https://togithub.com/tomperr) in
[mermaid-js/mermaid#4600
- Avoid downloading avtars everytime on docs:dev by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4603
- docs: Fix checkbox syntax by
[@&#8203;guilhermgonzaga](https://togithub.com/guilhermgonzaga) in
[mermaid-js/mermaid#4646
- Fix the "Edit this page on GitHub" link in Vitepress documentation for
the Mermaid Config pages by
[@&#8203;aloisklink](https://togithub.com/aloisklink) in
[mermaid-js/mermaid#4640
- Support MERMAID_RELEASE_VERSION in docs. by
[@&#8203;sidharthv96](https://togithub.com/sidharthv96) in
[mermaid-js/mermaid#4612
- Docs: update Latest News section by
[@&#8203;huynhicode](https://togithub.com/huynhicode) in
[mermaid-js/mermaid#4655
- added Typora to integrations list by
[@&#8203;kgilbert78](https://togithub.com/kgilbert78) in
[mermaid-js/mermaid#4666
- Docs: Corrects name of C4 link by
[@&#8203;Incognito](https://togithub.com/Incognito) in
[mermaid-js/mermaid#4660
- Fix a typo by [@&#8203;gjtorikian](https://togithub.com/gjtorikian) in
[mermaid-js/mermaid#4396

#### New Contributors

- [@&#8203;ryru](https://togithub.com/ryru) made their first
contribution in
[mermaid-js/mermaid#4567
- [@&#8203;ibrahimWassouf](https://togithub.com/ibrahimWassouf) made
their first contribution in
[mermaid-js/mermaid#4573
- [@&#8203;kislerdm](https://togithub.com/kislerdm) made their first
contribution in
[mermaid-js/mermaid#4577
- [@&#8203;leinelissen](https://togithub.com/leinelissen) made their
first contribution in
[mermaid-js/mermaid#4634
- [@&#8203;pierrickouw](https://togithub.com/pierrickouw) made their
first contribution in
[mermaid-js/mermaid#4496
- [@&#8203;mastersibin](https://togithub.com/mastersibin) made their
first contribution in
[mermaid-js/mermaid#4639
- [@&#8203;kgilbert78](https://togithub.com/kgilbert78) made their first
contribution in
[mermaid-js/mermaid#4666
- [@&#8203;Incognito](https://togithub.com/Incognito) made their first
contribution in
[mermaid-js/mermaid#4660
- [@&#8203;gjtorikian](https://togithub.com/gjtorikian) made their first
contribution in
[mermaid-js/mermaid#4396

**Full Changelog**:
mermaid-js/mermaid@v10.2.4...v10.3.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/levaintech/contented).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi4xMS4wIiwidXBkYXRlZEluVmVyIjoiMzYuMTEuMCIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants