typescript erasable codemod (paused)
tsec docs assumptions.md
2.7 kB · 104 lines
Markdown
at main

Thoughts on strategies taken for changes made #

I write these to share my thought process and reasoning for rewrite decisions made. They are the result of my prior experience and opinions formed from that. If your project needs to rewrite anything in a specifically different way, please make an issue! The selectors and infrastructure setup in this project should work well with whatever re-write strategies you'd like.

My goal is to allow a simple path to enabling erasableSyntaxOnly for all projects and not to be overly prescriptive with stylistic decisions.

Enums #

The default case in typescript could be something that is supported for anyone who needs exact parity, but this feels relatively uncommon and over-complicated.

enum LogLevel {
  Debug,
  Info,
  Warning,
  Error,
}

var LogLevel;
(function (LogLevel) {
  LogLevel[LogLevel["Debug"] = 0] = "Debug";
  LogLevel[LogLevel["Info"] = 1] = "Info";
  LogLevel[LogLevel["Warning"] = 2] = "Warning";
  LogLevel[LogLevel["Error"] = 3] = "Error";
})(LogLevel || (LogLevel = {}));

// evaluates to
var LogLevel = {
  "0": "Debug",
  "1": "Info",
  "2": "Warning",
  "3": "Error",
  "Debug": 0,
  "Info": 1,
  "Warning": 2,
  "Error": 3,
};

The bi-directional lookup here and implicit key: auto-incrementing num feels like logic abstracted away far enough that a sensible replacement could go relatively unnoticed while also simplifying the code.

Strategy Taken #

Use the string union approach with values that match the keys to preserve any lookups done for formatting the output value in practice. It is my belief that LogLevel. LogLevel.Debug being 0 is never/rarely the result of developer intent, but more often a hidden implementation detail that does not need preserving.

// before
enum LogLevel {
  Debug,
  Info,
  Warning,
  Error,
}

// after
const LogLevel = {
  Debug: "Debug",
  Info: "Info",
  Warning: "Warning",
  Error: "Error",
} as const;
type LogLevelType = typeof LogLevel[keyof typeof LogLevel];

Parameter Properties #

This one doesn't feel like much of an assumption and the change is relatively simple, so let's just looks at some code.

// before
class User {
  constructor(public name: string, public age: number) {}
}

// after
class User {
  public name: string;
  public age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

Relatively simple here... instead of implicitly adding properties from the constructor, they're explicitly defined as normal. If you'd like to see more examples, feel free to checkout the spec folder where inputs are .in.ts and output is .out.ts here.