Avoid silencing the type checker
Prove types to the compiler with checks instead of asserting them.
Use a type assertion, a non-null assertion, or @ts-expect-error only as a documented last resort, and never use @ts-ignore.
Implementation
- Replace
value!with a check that handles the missing case, such as an early return, a thrown error with context, or a default value. - Replace
value as Typeon data from outside with runtime validation, such as a type guard or schema. - Where an assertion is truly needed, such as a third-party type that is wrong, keep it in one small adapter and add a comment explaining why.
- Use
@ts-expect-errorwith a description of the reason, not@ts-ignore;@ts-expect-errorfails the build when the error it suppresses goes away. - Enforce with
@typescript-eslint/no-non-null-assertion,@typescript-eslint/consistent-type-assertions, and@typescript-eslint/ban-ts-commentwith'ts-expect-error': 'allow-with-description'. as constandsatisfiesare not assertions in this sense; they narrow or check types without overriding them.
Rationale
TypeScript's guarantees hold only where the compiler can check them.
An assertion replaces a check with a claim, and if the claim is wrong the error appears at runtime, often far from the assertion.
@ts-ignore hides every error on the next line, including new ones introduced later.
Examples
Application: A value that might be missing
Incorrect (counterexample):
const user = users.find((candidate) => candidate.id === id)!;
renderAvatar(user.avatar);
When no user matches, this crashes inside renderAvatar with an unhelpful error.
Correct:
const user = users.find((candidate) => candidate.id === id);
if (!user) {
throw new Error(`User ${id} not found`);
}
renderAvatar(user.avatar);
Application: Suppressing a compiler error
Incorrect (counterexample):
// @ts-ignore
const newUser = createUser('Gabriel');
Correct:
// @ts-expect-error: the library types require an object, but createUser also accepts a name (issue #123).
const newUser = createUser('Gabriel');
The comment explains the suppression, and the build fails once the library's types are fixed.
Validation
Run the lint rules above, and review each remaining assertion or @ts-expect-error for a comment that explains why no check is possible.
A documented assertion inside a type guard or adapter, after the checks that justify it, is not a violation.