Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Reviews TypeScript for the escape hatches that silently defeat the type checker — `any`, `as` assertions, non-null `!`, `@ts-ignore`, bare `object`/`Function`/`{}`, and unchecked index access — and names the type-safe replacement for each (unknown + narrowing, a validated type guard, generics, a precise shape). Use when reviewing or hardening TypeScript code for type safety. Do NOT use for tsconfig setup, non-TypeScript code, or debugging a compile error you are trying to make go away.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 572% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 542% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 554% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 557% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 548% | 0% |
TypeScript's checker only helps where you let it. A handful of constructs turn it off locally — and by default a reviewer waves them through ("compiles, looks fine") because the code does compile: that is exactly what the escape hatch bought. The bug it was hiding surfaces at runtime instead. This skill makes the review flag each escape hatch and name the concrete type-safe replacement, so the fix is actionable rather than a vague "add types".
Activate when the request hands you TypeScript code to review, tighten, or make type-safe — a diff, a file, a PR, a snippet — especially where the code reaches for one of the constructs below.
Do not activate for tsconfig / build configuration, for a language that isn't TypeScript, for explaining a type-system concept in the abstract, or for making a real compile error go away (suppressing an error is the anti-pattern this review flags, not the goal).
Each row: what the construct silences, and the type-safe replacement to recommend in its place.
| Construct | What it silences | Type-safe replacement | |---|---|---| | any (annotation, any[], Record<string, any>) | all checking on that value — and any spreads silently to everything it touches | unknown + narrowing before use, or a precise type / generic | | as assertion (x as T) | the compiler's own inference — no runtime check happens | validate into the type (a type guard or a schema parse), or fix the source type / use a generic | | non-null ! (x!, x!.foo) | the "possibly null/undefined" error, without proving it isn't | an explicit check that narrows (if (x) …), or make the type honest | | @ts-ignore | the next line's error forever, even after the code changes | fix the underlying type; if a suppression is truly unavoidable, use @ts-expect-error with a reason — it fails the build once the error is gone | | bare object / Function / {} | the actual shape — object = any non-primitive, Function returns any on call, {} = any non-null | a specific interface / Record<string, V>; a real call signature (a: A) => R; a named shape instead of {} | | unchecked index access (arr[i], map[key]) | that the element may be missing — the type claims it's always present | enable noUncheckedIndexedAccess so the result is T | undefined, then guard; or .at() / .get() / a presence check |
any → unknown. Both accept anything, but unknown forbids using the value until you narrow it, so the check moves to where the data actually enters. any leaks: one any return type quietly untype-checks every caller downstream.as → validate. data as ApiResponse asserts a shape the compiler never verified; if the payload differs, the mismatch is a runtime crash at first use. A type guard (function isApiResponse(v: unknown): v is ApiResponse) or a parse turns the claim into a checked fact. Watch for the double-assertion x as unknown as T — that is a deliberate detour around the compiler's refusal.! → narrow. ! is a promise to the compiler with no evidence. Replace with the check that supplies the evidence, or, if the value genuinely can't be absent, fix the type that says it can.@ts-ignore → @ts-expect-error. @ts-ignore silently keeps suppressing even after the underlying code is fixed, so dead suppressions accumulate. @ts-expect-error errors if the line stops having an error — it self-cleans. Neither is a substitute for fixing the type when you can.object/Function → a shape. These accept far more than intended. Function is the worst: calling it yields any, re-opening the hole. Name the real signature or interface.T | undefined. By default const row = rows[i] is typed as present even when i is out of range, so row.name type-checks and then throws at runtime. noUncheckedIndexedAccess makes the optionality visible so you must handle it.unknown + narrow, a type guard, a generic, a precise shape, an index guard).Reviewing:
tsfunction handle(payload: any) { const user = payload as SessionUser; return cache.get(user.id)!.token; }
Review:
payload: any — disables checking on payload and everything derived from it. Type the parameter unknown and narrow, or give it the precise inbound type.payload as SessionUser — an unverified assertion; if the payload isn't a SessionUser this is a runtime crash at user.id. Validate with a type guard or a schema parse instead of asserting.cache.get(user.id)! — the non-null ! hides that .get() can return undefined on a miss. Check the result (const entry = cache.get(user.id); if (!entry) …) before reading .token.Verdict: 3 escape hatches to replace before merge.
JSON.parse returns any inherently): accept it, but narrow immediately — assign to unknown and validate, don't let the any propagate.as for partial data: still a hatch; prefer a typed partial-builder over as so the fixture stays honest as the type evolves.@ts-expect-error with a reason: already the good form — don't flag it as if it were @ts-ignore.unknown already in use: that's the fix, not a finding — confirm it's narrowed before use rather than re-asserted with as.Other measured skills in the registry, with their headline benchmark lift.