Defensive Programming
SkillDev toolsType safety practices including exhaustive checks, explicit return types, Result-based error handling, and non-mutating array methods. Use when writing TypeScript logic that handles multiple cases or error conditions, or when sorting, reversing, splicing, or replacing an element (indexed assignment) in an array you did not create.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Defensive Programming skill
What this skill tells your AI
The instructions your AI receives, as published by trezor/trezor-suite in skills/defensive-programming/SKILL.md and read by ahel’s review.
Do not fall back to default
Whenever possible, cover all cases. If a new case is added in the future, TypeScript should force the developer to set behavior for it.
Force explicit return types
Makes sure all cases are covered in a function.
// TS Error: Function lacks ending return statement and return type does not include 'undefined'
export const isEnabled = (status: 'a' | 'b' | 'c'): boolean => {
if (status === 'a') {
return true;
}
if (status === 'b') {
return false;
}
};
Use exhaustive switch
Makes sure all cases are covered in a switch statement.
// TS Error: Argument of type '"c"' is not assignable to parameter of type 'never'
export const isEnabled = (status: 'a' | 'b' | 'c') => {
switch (status) {
case 'a':
return true;
case 'b':
return false;
default:
return exhaustive(status);
}
};
Use type-mapping technique
Alternative to an exhaustive switch statement.
type Schema = {
a: number;
b: number;
};
// TS Error: Property 'b' is missing in type '{ a: () => string; }' but required in type '{ a: () => void; b: () => void; }'.
const result: { [K in keyof Schema]: () => void } = {
a: () => console.log('This is A'),
};
Prefer the non-mutating array methods to copy-then-mutate
[...items].sort() and items.slice().sort() are workarounds for sort mutating in place, and they only work if you remember the copy. toSorted, toReversed, toSpliced and with return a new array, so forgetting is not an option — and forgetting matters here, because the arrays in play are usually a selector result or Redux state that something else is still holding. Nothing will catch it either: immutableCheck is false in both stores (suite, native), so a mutated slice surfaces later as a stale render or a wrong total, far from the .sort() that caused it.
// bad - two steps to say "sorted copy", and `.sort()` mutates whatever it is handed
const sorted = [...accounts].sort(byBalance);
// good - one call, nothing to forget
const sorted = accounts.toSorted(byBalance);
Do not use exceptions
Unless failures are unpredictable, pass errors via return and do not throw. Throwing exceptions is not type-safe. There is a Result type that shall be used.
Bad:
try {
const result = await action();
} catch (error) {
// Possible errors cannot be typed
// ...
}
Good:
const result = await action();
if (result.error) {
const { type } = result.error;
switch (type) {
case 'ErrorA':
// ... do stuff
case 'ErrorB':
// ... do different stuff
default:
return exhaustive(type);
}
}
Signals
- GitHub stars
- 1k
- Forks
- 375
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
defensive-programming- Source
- github.com/trezor/trezor-suite