iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🤓

better-typescript-lib v2: Even More Convenient with TypeScript 4.5

に公開

One of the new features in TypeScript 4.5 Beta released today is that replacing the standard library becomes easier than ever before.

I have been developing better-typescript-lib, which makes the TypeScript standard library safer by eliminating any. I have now prepared a beta version of v2.0.0 (2.0.0-beta) that supports TypeScript 4.5.

In this article, in addition to a brief introduction to better-typescript-lib, I will explain the features of TypeScript 4.5 and the changes they brought to better-typescript-lib.

About better-typescript-lib

better-typescript-lib makes the TypeScript standard library more type-safe. Examples of where it improves safety include JSON.parse and eval. In standard TypeScript, the return values of these are any.

// any 😩
const obj = JSON.parse('{"foo": 123}');

console.log(obj.foo); // 123

When you introduce better-typescript-lib, the return value of JSON.parse becomes JSONValue, and the return value of eval becomes unknown. JSONValue is a union type of all values that can be represented as JSON. This makes handling them a bit more tedious since they are no longer any, but in exchange, it becomes type-safe.

// JSONValue 😁
const obj = JSON.parse('{"foo": 123}');

if (isFooObject(obj)) {
  console.log(obj.foo); // 123
}

function isFooObject(obj: JSONValue): obj is { foo: number } {
  return isPropertyAccessible(obj) && typeof obj.foo === "number";
}
function isPropertyAccessible(obj: unknown): obj is Record<string, unknown> {
  return obj !== null;
}

Writing it this way might seem quite cumbersome, but this is what should have been done in the first place. By eliminating any, you can properly notice things that need to be handled. By using better-typescript-lib, you can discover mistakes that were previously hidden by any.

For more details, please also see this article I wrote previously.

https://qiita.com/uhyo/items/18458646e8aae25207db

Conventional Installation Method for better-typescript-lib

While better-typescript-lib is convenient, its installation used to be somewhat difficult. It was necessary to disable the loading of the standard library provided by TypeScript and manually load better-typescript-lib.

Specifically, you had to run npm i -D better-typescript-lib, write "noLib": true in tsconfig.json, and then load it from your application code as follows. This wasn't very elegant and felt quite hacky.

/// <reference path="./node_modules/better-typescript-lib/lib.es5.d.ts" />
/// <reference path="./node_modules/better-typescript-lib/lib.dom.d.ts" />

New Installation Method for better-typescript-lib and How it Works

In better-typescript-lib v2, which supports TypeScript 4.5 and above, installation is as simple as running npm install. In other words, you just need to run the following; you don't even need to modify tsconfig.json, let alone load it manually. It's very straightforward.

npm i -D better-typescript-lib@2.0.0-beta

What makes this possible is the following feature introduced in TypeScript 4.5:

https://github.com/microsoft/TypeScript/pull/45771

This feature allows a package named @typescript/lib-[lib], if installed, to be loaded instead of the standard library's lib.[lib].d.ts. For example, the previous behavior was that if "target": "es2015" was specified in tsconfig.json, the lib.es2015.d.ts included with TypeScript would be loaded. Now, if @typescript/lib-es2015 is installed, it will be loaded instead.

This allows you to replace the type definitions loaded by TypeScript by installing improved type definitions under names like @typescript/lib-es2015.

Of course, the TypeScript team owns the @typescript scope on npm. Third parties cannot publish packages under these names on their own. Fortunately, however, npm (and other package managers that support package.json) supports a feature to install packages under aliases. The package.json of better-typescript-lib contains the following:

  "dependencies": {
    "@typescript/lib-dom": "npm:@better-typescript-lib/dom@2.0.0-beta",
    "@typescript/lib-es2015": "npm:@better-typescript-lib/es2015@2.0.0-beta",
    "@typescript/lib-es2016": "npm:@better-typescript-lib/es2016@2.0.0-beta",
    "@typescript/lib-es2017": "npm:@better-typescript-lib/es2017@2.0.0-beta",
    "@typescript/lib-es2018": "npm:@better-typescript-lib/es2018@2.0.0-beta",
    "@typescript/lib-es2019": "npm:@better-typescript-lib/es2019@2.0.0-beta",
    "@typescript/lib-es2020": "npm:@better-typescript-lib/es2020@2.0.0-beta",
    "@typescript/lib-es2021": "npm:@better-typescript-lib/es2021@2.0.0-beta",
    "@typescript/lib-es5": "npm:@better-typescript-lib/es5@2.0.0-beta",
    "@typescript/lib-esnext": "npm:@better-typescript-lib/esnext@2.0.0-beta",
    "@typescript/lib-header": "npm:@better-typescript-lib/header@2.0.0-beta",
    "@typescript/lib-scripthost": "npm:@better-typescript-lib/scripthost@2.0.0-beta",
    "@typescript/lib-webworker": "npm:@better-typescript-lib/webworker@2.0.0-beta"
  }

This means, for example, installing @better-typescript-lib/dom@2.0.0-beta under the alias @typescript/lib-dom. By doing this, you can install any package under the name @typescript/lib-[lib]. We leverage this to make TypeScript load our custom type definitions (this might feel a bit like a hack, but it's the method officially recommended by TypeScript).

The reason these @typescript/lib-[lib] packages become visible to the TypeScript compiler just by installing better-typescript-lib is that it relies on the specification where the contents of node_modules are flattened. It might not work in environments like PnP, but future support may be considered depending on demand.

Incidentally, this feature was likely introduced in TypeScript to realize the @types/web concept (see the following issue):

https://github.com/microsoft/TypeScript/issues/44795

While the TypeScript standard library changes with each TypeScript version update, changes to lib.dom.d.ts in particular were often a cause of breaking backward compatibility. By installing @types/web as @typescript/lib-dom, it becomes possible to manage versions independently and avoid being affected by TypeScript version updates. There were several ways to achieve this, but the current method of using @typescript/lib-[lib] was chosen for the advantage of being able to fully delegate version management of dependencies to existing package managers. The ease of installing better-typescript-lib is a direct beneficiary of this.

Summary

TypeScript 4.5 introduced a new feature to easily replace standard library type definitions. better-typescript-lib, which provides a more type-safe standard library than the default, has immediately adopted this feature in its next major version, making installation much easier.

GitHubで編集を提案

Discussion