-
-
Notifications
You must be signed in to change notification settings - Fork 37
Add entry forany, as usage in TypeScript Guidelines doc #47
Copy link
Copy link
Closed
Labels
Description
Motivation
- There are currently hundreds of instances of
anytype usage andasassertions spread throughout metamask-extension and core. - For future maintainability, there should be clear guidelines defining the limited cases where
any,asusage is intentional and acceptable vs. the majority of cases whereany,asshould be corrected and removed.
References
Research
Catalogue any, as usage in core, metamask-extension and apply fixes where appropriate.
any
Avoiding any
-
anydoesn't provide the widest type, or any type at all. It's a compiler setting to disable type checking for the type it's assigned to, which kneecaps type safety by definition. -
unknownis the universal supertype i.e. the widest possible type.- If what's needed is a type that could be "anything,"
unknownshould be used. anyandunknownare interchangeable when typing the assignee (every type is assignable to both).anyis assignable to all types, whileunknownis only assignable tounknown.- If replacing
anywithunknowndoesn't work, the typing likely has underlying issues that shouldn't be silenced.
- If what's needed is a type that could be "anything,"
-
If
anyis used, and errors are introduced (or altered) by future changes to the code,- the new or changed warnings will be suppressed,
- and the code will fail silently.
- "dangerous because infects all surrounding and downstream code ..."
-
If no good typing solution can be found, using forced type casting with the
askeyword or evenas unknown asis much preferable to usingany:- At least we get working intellisense, autocomplete.
- We also get an indication of the expected type as intended by the author.
-
Function supertype (assignable to any function):
(...args: any[]) => any // bad
(...args: never[]) => unknown // goodAcceptable usages of any
- Assigning new properties to an object or class at runtime:
(this as any)[key] = obj[key]- Within generic constraints:
messenger extends RestrictedControllerMessenger<N, any, any, string, string>-
- In general, using
anyin this context is not harmful in the same way that it is in other contexts, as theanytypes are not directly assigned to any specific variable, and only function as constraints. - That said, narrower constraints provide better type safety and intellisense.
- In general, using
- Catching errors:
catchonly acceptsanyandunknownas the error type .- Recommended: Use
unknownwith type guards likeisJsonRpcError.
- In tests, for mocking or to intentionally break features.
- Recommended: Provide accurate typing through assertions wherever possible.
as
Avoiding as
Consider replacing as casting with the following, in order of preference:
- 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.
- In some cases, type assertions can be replaced with type narrowing with type guards and null checks.
- If a type constraint needs to be imposed, prefer the
satisfieskeyword (for TypeScript v4.9+), since it allows type inference to a more narrow type. :type declarations also enable type constraints to be imposed, but don't allow narrowing by inference.asassertions intended to exclude an empty/nullable type can be replaced by a nullish coalescing operator converting nullable values into an acceptable empty value that doesn't pollute the variable's type signature.
(arr as string[])
(arr ?? [])Acceptable usages of as
- To define user-defined type guards: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
- To type inputs/outputs that are defined at runtime, provided that schema validation is performed with type guards and unit tests.
- e.g. The output of
JSON.parse()orawait response.json()for a known JSON input.
- e.g. The output of
- In tests, for mocking objects.
as unknown ascan be used where the mocked object and expected type are significantly divergent e.g. due to omitting required properties.
{ ...mockObject } as unknown as TargetTypeReactions are currently unavailable