Q36 of 38 · TypeScript
What techniques do you use to prevent `any` from spreading through a TypeScript codebase?
Short answer
Short answer: `any` is contagious — it propagates through assignments and function returns. Prevent it with: `noImplicitAny` + `strict`, ESLint `@typescript-eslint/no-explicit-any`, using `unknown` at boundaries, replacing `any[]` with generics, and auditing `as` assertions. Track coverage with `typescript-strict-plugin` per file.
Detail
The insidious property of any is that it silently flows downstream — a single any in a utility function can infect every consumer's types.
Prevention strategies:
noImplicitAny: The first line of defence. Forces explicit types where TypeScript cannot infer them. Enabled by
strict: true.ESLint @typescript-eslint/no-explicit-any: Bans writing
anyexplicitly in source. Pair with@typescript-eslint/no-unsafe-assignment(warns when assigning fromany). These catch whatnoImplicitAnymisses.Use unknown at boundaries: Functions that receive external data (JSON.parse, API calls, catch blocks) should use
unknownnotany. This forces narrowing at the point of use.Generic functions over typed-any arrays:
processItems<T>(items: T[]): T[]instead ofprocessItems(items: any[]): any[]preserves types through the function.Audit type assertions:
as SomeTypecan maskany. Usesatisfiesorunknown+ narrowing instead.Detect existing spread:
typescript-strict-plugin(per-file strict configuration) orgrep -r ': any'to audit. Track reduction over sprints as a metric.
// EXAMPLE
// ANTI-PATTERN: any at boundary, spreads everywhere
function parseConfig(raw: string): any { // any returns infect callers
return JSON.parse(raw);
}
const config = parseConfig("{}");
config.nonExistent.deeply.nested; // no error — any swallows it
// BETTER: unknown + narrowing
interface Config { baseUrl: string; timeout: number; }
function parseConfig2(raw: string): Config {
const val: unknown = JSON.parse(raw);
if (
typeof val === "object" && val !== null &&
"baseUrl" in val && "timeout" in val
) {
return val as Config; // one controlled assertion with prior narrowing
}
throw new Error("Invalid config shape");
}
// ESLint config — .eslintrc.json
{
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-return": "warn"
}
}