TS7006: Parameter 'X' implicitly has an 'any' type fires under noImplicitAny when the compiler cannot infer a parameter’s type. Common triggers: standalone arrow functions, event handlers passed to addEventListener directly, and callbacks passed to loosely-typed third-party APIs. Fix by annotating the parameter or providing enough context for inference. Here is the complete 2026 fix guide.
Why TS7006 happens
TypeScript’s noImplicitAny rule (part of strict) forbids parameters whose type it cannot deduce. If the parameter appears in a lambda that is not directly assigned to a typed slot, TypeScript has no context to infer from. TS7006 tells you to fix the missing context.
Fix 1: annotate the parameter
// WRONG
const double = (n) => n * 2;
// TS7006: Parameter 'n' implicitly has an 'any' type.
// FIX: annotate
const double = (n: number) => n * 2;Fix 2: array method callbacks
const nums = [1, 2, 3, 4];
// Usually inferred correctly
const doubled = nums.map(n => n * 2); // n is number
// But if the array is broader
const mixed = [1, "two", 3]; // (number | string)[]
mixed.forEach(x => console.log(x.toUpperCase()));
// TS2339 because x may be number
// FIX: type-guard first
mixed.forEach(x => {
if (typeof x === "string") console.log(x.toUpperCase());
});Fix 3: addEventListener callbacks
// WRONG when destructuring event
document.addEventListener("click", ({ target }) => {
console.log(target.tagName);
// TS7006 or TS18047 depending on version
});
// FIX 1: use full event with type
document.addEventListener("click", (e: MouseEvent) => {
const target = e.target as HTMLElement | null;
console.log(target?.tagName);
});
// FIX 2: currentTarget for element that listener is attached to
button.addEventListener("click", (e) => {
// e.currentTarget is typed as HTMLButtonElement here
console.log(e.currentTarget.textContent);
});Fix 4: callback param with a typed target
// Define the callback type
type OnChange = (value: string, index: number) => void;
// Now TS infers correctly
const handler: OnChange = (value, index) => {
console.log(value.toUpperCase(), index * 2); // works, no TS7006
};
// Even without a variable, function param type gives context
function subscribe(cb: (message: string) => void) {}
subscribe(message => console.log(message.length)); // message inferred as stringDebugging checklist
- Check your
tsconfig.jsonhasnoImplicitAnyorstrict. Both trigger TS7006. - Look at what surrounds the function. If it is assigned to a variable with no type, add an annotation.
- If the parameter comes from a third-party library, check that
@types/*is installed and up-to-date. - For legacy JavaScript files being migrated, use
allowJsandcheckJsgradually. Do not disablenoImplicitAny.
Common mistakes when fixing TS7006
- Annotating with
any. Silences TS7006 but defeats the purpose. Use a real type. - Disabling
noImplicitAny. Removes an entire class of type safety. Fix the actual annotations. - Passing untyped callback into a well-typed library. If the library expects
(x: number) => void, just type your callback to match. - Casting event targets without type guard.
e.target as HTMLInputElementcan crash if target is a different element type.
Debugging TypeScript errors systematically
TypeScript errors follow a predictable pattern once you understand the compiler’s logic. Before jumping to fixes, run through this diagnostic checklist.
- Read the FULL error message. The compiler tells you both the actual type it sees and the expected type. Match them side by side to spot the mismatch.
- Check strictNullChecks and strict mode. Many errors trace back to
strict: truein tsconfig.json. If you inherited a codebase with strict mode on, expect more errors than in loose mode. - Look at inferred types with hover. VS Code shows inferred types on hover. Compare what TypeScript sees vs what you assumed.
- Use // @ts-expect-error strategically. When you know better than the compiler temporarily, mark the line. TypeScript flags the annotation when the underlying issue is fixed.
- Isolate the failing line in a minimal reproducer. A standalone
.tsfile with hardcoded inputs often surfaces the root cause faster than debugging in place.
Common TypeScript configuration issues
Many “why does this not compile” issues trace back to tsconfig.json, not your code.
- strict flag toggles many checks at once.
strict: trueenables strictNullChecks, noImplicitAny, strictFunctionTypes, and more. Turning on strict without preparing your code produces hundreds of errors. - Version mismatch between global and project TypeScript. Different TypeScript versions produce different errors. Pin the version in package.json for consistency.
- skipLibCheck can hide real issues. Setting
skipLibCheck: truespeeds up compilation but hides type mismatches in dependencies. Fine for CI but keep it off during development. - Path aliases and moduleResolution mismatches. The
pathsconfig in tsconfig only works with the right moduleResolution setting. Match “bundler” for Vite, “node16” for Node. - Ambient declarations vs modules. Files with imports are modules; files without imports are ambient scripts. Some errors happen because a file is treated as one when you expected the other.
Production TypeScript best practices
Once you get past errors and ship code, keep these habits to prevent recurring problems.
- Enable strict mode from day 1 on new projects. Migrating to strict later is 10x more work.
- Never use
anywithout a good reason.unknownis safer and forces you to narrow before use. - Avoid type assertions (
as) except at API boundaries. Assertions bypass the type system. Use them sparingly at edges (JSON parsing, DOM, external APIs). - Prefer type over interface for new code. Type aliases work everywhere interfaces do, plus support unions, intersections, and mapped types.
- Test type-level logic. Tools like
expect-typeortsdlet you write tests for your type definitions.
Learning resources for TypeScript
Beyond fixing the current error, deeper TypeScript knowledge pays dividends for years. Recommended starting points:
- Official Handbook. typescriptlang.org/docs/handbook. The canonical reference, well-written and up-to-date.
- Total TypeScript by Matt Pocock. Free tutorials and paid courses. Best for going from junior to advanced TypeScript.
- Type Challenges GitHub repo. Interactive problems that build type-level programming skills.
- TypeScript Deep Dive. Free online book, especially good for the “why” behind features.
- Try in the playground. typescriptlang.org/play lets you experiment with types and see errors immediately.
TypeScript version considerations
Compiler errors can change slightly between TypeScript versions. TypeScript 5.0+ introduced better error messages for many common issues. If you are on 4.x, upgrading may automatically improve diagnostic quality without any code change. Check your package.json for the pinned typescript version, then compare against the release notes at github.com/microsoft/TypeScript/releases before upgrading in production.
Official documentation
Frequently Asked Questions
Should I ever use any?
Almost never. Use unknown when the type is truly unknown at compile time and validate at runtime. any disables type checking entirely.
Can I disable noImplicitAny temporarily?
Yes with "noImplicitAny": false in tsconfig. Not recommended even temporarily. It masks real bugs and creates a slow accumulation of untyped code.
What is the difference between any and unknown?
any disables type checking. unknown keeps it. You cannot use an unknown value without first narrowing it with a type guard. Prefer unknown for truly dynamic input.
Are third-party libraries triggering this?
Sometimes. If @types/some-lib is stale or missing, callback parameters lose their inferred type. Update @types/* packages or add module augmentation for missing definitions.
Does TS7006 show up in JSX?
Yes when you write inline event handlers on custom components without proper prop types. Fix by typing the prop as (e: React.MouseEvent) => void or similar on the component.
