Skip to content

Commit ecf59b1

Browse files
committed
stash
1 parent 0eab0d6 commit ecf59b1

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

docs/typescript.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,100 @@ async function removeAccount(address: Hex): Promise<KeyringControllerState> {
3939
return this.fullUpdate();
4040
}
4141
```
42+
43+
## Overriding Types
44+
45+
- Order of preference: `infer` > `:` > `as` > `any`
46+
- Provide explanation via comment when explicitly casting types or overriding related eslint rules (e.g. forbid `any`, forbid `non-null assertions`)
47+
48+
### 1. Type Inference
49+
50+
- Prefer type inference: Inferences are responsive to changes in code, while assertions rely on brittle hard-coding. While TypeScript will throw type errors against some unsafe or structurally unsound type assertion scenarios, it will generally accept the user-supplied type without type-checking. This can cause silent failures where errors are suppressed.
51+
52+
- Generic Parameters
53+
- `Object.entries`
54+
- `Array.prototype.reduce`
55+
- Type Narrowing
56+
- In some cases, type assertions can be replaced with type narrowing with type guards and null checks.
57+
58+
### 2. Type Annotations (`:`)
59+
60+
- If a type constraint needs to be imposed, prefer the `satisfies` keyword (for TypeScript v4.9+), since it allows type inference to a more narrow type.
61+
- `:` type declarations also enable type constraints to be imposed, but don't allow narrowing by inference.
62+
63+
### 3. Type Assertions (`as`, `!`)
64+
65+
- `as` assertions intended to exclude an empty/nullable type (e.g. `| undefined`) can be replaced with a nullish coalescing operator providing an acceptable fallback empty value that doesn't pollute the variable's type signature.
66+
67+
```ts
68+
arr: string[] | undefined
69+
(arr as string[]) // 🚫
70+
(arr ?? []) //
71+
72+
s: string | undefined
73+
(s as string) // 🚫
74+
(s ?? '') //
75+
```
76+
77+
- Acceptable usages of `as`
78+
- To prevent or fix `any` usage:
79+
- At least we get working intellisense, autocomplete.
80+
- We also get an indication of the expected type as intended by the author.
81+
- To define user-defined type guards: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
82+
- To type inputs/outputs that are defined at runtime, provided that schema validation is performed with type guards and unit tests.
83+
- e.g. The output of `JSON.parse()` or `await response.json()` for a known JSON input.
84+
- e.g. The type of a JSON file.
85+
- In tests, for mocking or to exclude irrelevant but required properties from an input object.
86+
- Recommended: Provide accurate typing wherever possible.
87+
88+
- Acceptable usages of `!`
89+
- If type narrowing is not inferring the correct type.
90+
- If the variable is otherwise guaranteed to be non-nullable.
91+
- Preferrably, leave a comment explaining why.
92+
93+
### 4. Disable Type Checking (`any`)
94+
95+
- `any` doesn't actually represent the widest type. It's a compiler setting to disable type checking for the value and/or type to which it's assigned.
96+
97+
- If `any` is used, and errors are introduced (or altered) by future changes to the code,
98+
- The new or changed warnings will be suppressed, and the code will **fail silently**.
99+
100+
- `any` infects all surrounding and downstream code.
101+
- For instance, if you have a function that is declared to return any which actually returns an object, all properties of that object will be any.
102+
103+
- `unknown` is the universal supertype i.e. the widest possible type.
104+
- When typing the assignee, `any` and `unknown` are interchangeable (every type is assignable to both).
105+
- When typing the assigned, `unknown` can't replace `any`, as `unknown` is only assignable to `unknown`.
106+
- If replacing `any` with `unknown` doesn't work, the typing likely has underlying issues that _shouldn't be silenced_.
107+
108+
- Function supertype (assignable to any function):
109+
110+
```ts
111+
(...args: any[]) => any // 🚫
112+
(...args: never[]) => unknown //
113+
```
114+
115+
#### Acceptable use cases
116+
117+
- Assigning new properties to an object or class at runtime:
118+
119+
```ts
120+
(this as any)[key] = obj[key];
121+
```
122+
123+
- Within generic constraints:
124+
125+
```ts
126+
messenger extends RestrictedControllerMessenger<N, any, any, string, string>
127+
```
128+
129+
- In general, using `any` in this context is not harmful in the same way that it is in other contexts, as the `any` types are not directly assigned to any specific variable, and only function as constraints.
130+
- That said, narrower constraints provide better type safety and intellisense.
131+
132+
- Catching errors:
133+
- `catch` only accepts `any` and `unknown` as the error type.
134+
- Recommended: Use `unknown` with type guards like `isJsonRpcError`.
135+
- Avoid typing an error object with `any` if the object is used elsewhere instead of just being thrown.
136+
137+
- In tests, for mocking or to intentionally break features.
138+
- Recommended: Provide accurate typing wherever possible.

0 commit comments

Comments
 (0)