周报@2023-04-13

TypeScript 4.9 的新特性

The satisfies Operator

https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/#the-satisfies-operator

TypeScript developers are often faced with a dilemma: we want to ensure that some expression matches some type, but also want to keep the most specific type of that expression for inference purposes.

For example:

ts
1// Each property can be a string or an RGB tuple.
2const palette = {
3    red: [255, 0, 0],
4    green: "#00ff00",
5    bleu: [0, 0, 255]
6//  ^^^^ sacrebleu - we've made a typo!
7};
8
9// We want to be able to use array methods on 'red'...
10const redComponent = palette.red.at(0);
11
12// or string methods on 'green'...
13const greenNormalized = palette.green.toUpperCase();

Notice that we’ve written bleu, whereas we probably should have written blue. We could try to catch that bleu typo by using a type annotation on palette, but we’d lose the information about each property.

typescript
1type Colors = "red" | "green" | "blue";
2
3type RGB = [red: number, green: number, blue: number];
4
5const palette: Record<Colors, string | RGB> = {
6    red: [255, 0, 0],
7    green: "#00ff00",
8    bleu: [0, 0, 255]
9//  ~~~~ The typo is now correctly detected
10};
11
12// But we now have an undesirable error here - 'palette.red' "could" be a string.
13const redComponent = palette.red.at(0);

The new satisfies operator lets us validate that the type of an expression matches some type, without changing the resulting type of that expression. As an example, we could use satisfies to validate that all the properties of palette are compatible with string | number[]:

typescript
1type Colors = "red" | "green" | "blue";
2
3type RGB = [red: number, green: number, blue: number];
4
5const palette = {
6    red: [255, 0, 0],
7    green: "#00ff00",
8    bleu: [0, 0, 255]
9//  ~~~~ The typo is now caught!
10} satisfies Record<Colors, string | RGB>;
11
12// Both of these methods are still accessible!
13const redComponent = palette.red.at(0);
14const greenNormalized = palette.green.toUpperCase();

satisfies can be used to catch lots of possible errors. For example, we could ensure that an object has all the keys of some type, but no more:

typescript
1type Colors = "red" | "green" | "blue";
2
3// Ensure that we have exactly the keys from 'Colors'.
4const favoriteColors = {
5    "red": "yes",
6    "green": false,
7    "blue": "kinda",
8    "platypus": false
9//  ~~~~~~~~~~ error - "platypus" was never listed in 'Colors'.
10} satisfies Record<Colors, unknown>;
11
12// All the information about the 'red', 'green', and 'blue' properties are retained.
13const g: boolean = favoriteColors.green;

Maybe we don’t care about if the property names match up somehow, but we do care about the types of each property. In that case, we can also ensure that all of an object’s property values conform to some type.

typescript
1type RGB = [red: number, green: number, blue: number];
2
3const palette = {
4    red: [255, 0, 0],
5    green: "#00ff00",
6    blue: [0, 0]
7    //    ~~~~~~ error!
8} satisfies Record<string, string | RGB>;
9
10// Information about each property is still maintained.
11const redComponent = palette.red.at(0);
12const greenNormalized = palette.green.toUpperCase();

For more examples, you can see the issue proposing this and the implementing pull request. We’d like to thank Oleksandr Tarasiuk who implemented and iterated on this feature with us.

布尔逻辑运算符 - AND、OR、NOT、XOR

逻辑布尔运算符使用 bool 操作数执行逻辑运算。 运算符包括一元逻辑非 (!)、二元逻辑 AND (&)、OR (|) 以及异或 (^),二元条件逻辑 AND (&&) 和 OR (||)。

JavaScript 的 BigInt 异或运算非常差,建议 Rust 重写这部分

error TS2310: Type 'Matchers<R, T>' recursively references itself as a base type.

how to fix it? add "skipLibCheck": true to your tsconfig.json

json
1{
2  "compilerOptions": {
3    "target": "ESNext",
4    "module": "ESNext",
5    "moduleResolution": "node",
6    "allowSyntheticDefaultImports": true,
7    "alwaysStrict": true,
8    "sourceMap": true,
9    "forceConsistentCasingInFileNames": true,
10    "noFallthroughCasesInSwitch": true,
11    "noImplicitReturns": true,
12    "noUnusedLocals": true,
13    "noUnusedParameters": true,
14    "noImplicitAny": false,
15    "noImplicitThis": false,
16    "strictNullChecks": false,
17    "strict": true,
18    "outDir": "dist",
19    "baseUrl": ".",
20    "paths": {
21      "@/*": ["./src/*"]
22    },
23    "skipLibCheck": true
24  },
25  "include": ["src/**/*.ts"]
26}