NoImplicitAny in TypeScript

noImplicitAny forces you to annotate types wherever TypeScript cannot infer them. Learn what it does, why it catches hidden bugs, and how to fix the errors it surfaces.

5 min read

noImplicitAny is a TypeScript compiler flag that catches places where the compiler would silently fall back to the any type because it has no other information. When enabled, TypeScript requires an explicit type annotation wherever inference is not possible.

It is one of the flags enabled by strict mode, and it is arguably the one that changes how you write TypeScript the most. Without it, you can write functions with untyped parameters and the compiler says nothing. With it, every piece of your code has a known type.

What the flag catches

When TypeScript encounters a variable or parameter it cannot assign a type to, it defaults to any. The any type opts out of type-checking entirely: you can call any method on it, pass it anywhere, and assign anything to it, all without errors.

Here is what happens without noImplicitAny. This function compiles silently even though the parameters have no types at all:

typescripttypescript
function formatName(first, last) {
  return `${last}, ${first}`;
}
 
console.log(formatName("Ada", "Lovelace"));

Both parameters are implicitly any, which is dangerous. It means you can pass any value and the compiler will not stop you. This wrong call also compiles without a single complaint:

typescripttypescript
formatName(42, true);

That call would produce "true, 42" at runtime with no compile-time warning whatsoever. The function signature told the compiler nothing about what it expects, so it allowed everything.

With noImplicitAny enabled, the original function produces this error at each parameter declaration:

texttext
Parameter 'first' implicitly has an 'any' type.
Parameter 'last' implicitly has an 'any' type.

TypeScript reports one error per parameter because it checks each one independently for a usable type. The fix is to annotate each parameter with the type it should accept:

typescripttypescript
function formatName(first: string, last: string) {
  return `${last}, ${first}`;
}

Now formatName(42, true) is a compile error because number and boolean are not assignable to string.

Where noImplicitAny surfaces errors

Function parameters with no annotation

This is the most common trigger. A parameter with no type annotation and no default value has no way for TypeScript to know its type. Consider this untyped function:

typescripttypescript
function calculate(x, y) {
  return x + y;
}

The compiler reports that both parameters implicitly have an any type because it cannot guess what x and y should be. The fix is straightforward: annotate them both with the types you intend:

typescripttypescript
function calculate(x: number, y: number): number {
  return x + y;
}

Callback parameters with no contextual type

When you pass a callback to a typed function like .map(), TypeScript infers the parameter types from context. But a standalone function with untyped parameters gets flagged:

typescripttypescript
const handler = (event) => {
  console.log(event.target);
};

There is no context telling TypeScript what event is. The compiler reports that event implicitly has an any type. The fix is to provide a type annotation:

typescripttypescript
const handler = (event: MouseEvent) => {
  console.log(event.target);
};

Variables that cannot be inferred

TypeScript usually infers variable types from their initial value. But when a variable is declared without an initializer and without a type annotation, it cannot be inferred:

typescripttypescript
let result;
result = 42;
result = "hello";

The compiler warns that result implicitly has an any type in some locations. The fix is to annotate it or initialize it immediately:

typescripttypescript
let result: number;
result = 42;

Or simply combine declaration and initialization so TypeScript can infer the type from the value.

When inference is enough

The noImplicitAny flag does not force you to annotate everything. When TypeScript can figure out the type from context, no annotation is needed. Here are examples where inference works and the flag is silent:

typescripttypescript
const name = "Ada";
const scores = [95, 87, 91];
const doubled = scores.map((n) => n * 2);
 
function greet(name: string) {
  return `Hello, ${name}!`;
}

The variable name is inferred as string from its value. The scores array is inferred as number[] from its elements. The map callback parameter n is inferred as number from the array type. None of these trigger noImplicitAny because the compiler has enough information.

noImplicitAny during migration

If you are adding TypeScript to an existing JavaScript project, enabling noImplicitAny can produce hundreds of errors at once. The practical approach is to enable strict mode but disable this one flag temporarily, then re-enable it one file at a time as you add annotations:

jsonjson
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": false
  }
}

This keeps all other strict checks active (null checks, strict function types, property initialization) while letting you handle implicit any gradually. Once a file or directory is fully annotated, turn the flag back on for that scope via a more specific tsconfig if needed.

noImplicitAny vs explicit any

The noImplicitAny flag does not ban the any type. It only catches places where the compiler silently uses any because it has no choice:

CasenoImplicitAny result
Parameter or variable with no annotation and no inferable typeError, because the fallback to any was accidental
Parameter or variable annotated as : anyAllowed, because the any was a deliberate choice

You can still write any explicitly when you genuinely need to:

typescripttypescript
function parseJSON(input: string): any {
  return JSON.parse(input);
}

This compiles without error because the any is intentional. The flag catches accidental any, not deliberate any. If you want to ban even explicit any, use a lint rule like ESLint's no-explicit-any instead. That is a linting concern, not a compiler concern.

How it fits into strict mode

The noImplicitAny flag works alongside strictNullChecks to ensure your code has known types everywhere. First, noImplicitAny makes sure every value has a type (either inferred or annotated). Then strictNullChecks ensures null and undefined are handled explicitly within that type system.

Together they eliminate two of the largest sources of runtime errors in JavaScript: values that silently become any and null values that are used without checking.

For the bigger picture of how all strict checks work together, see strict mode in TypeScript. For an overview of the tool that enforces these checks, see the TypeScript compiler explained.

Rune AI

Rune AI

Key Insights

  • noImplicitAny errors when TypeScript would silently infer any for a variable or parameter.
  • The fix is always to add a type annotation or let TypeScript infer from context.
  • It is part of strict: true but can be disabled individually during migrations.
  • It does not ban explicit any, only accidental any.
  • Function parameters are the most common place to see noImplicitAny errors.
RunePowered by Rune AI

Frequently Asked Questions

Is noImplicitAny the same as banning the any type?

No. noImplicitAny only catches places where TypeScript silently falls back to any because it cannot figure out the type. You can still write any explicitly when you need to. The flag catches accidental any, not intentional any.

Why does TypeScript infer any in the first place?

TypeScript infers any when it has no type information to work with. For example, a function parameter with no annotation and no contextual clues cannot be given a more specific type.

Can noImplicitAny cause issues with third-party libraries?

Rarely. If a library has no type declarations, imports from it resolve to any. noImplicitAny does not flag these. It only catches missing annotations in your own code. For untyped libraries, create a .d.ts file or install types from DefinitelyTyped.

Conclusion

noImplicitAny is one of the most impactful strict-mode flags. It forces every variable and parameter to have a known type, either through inference or explicit annotation. The result is code where the compiler always knows the shape of your data, which means better autocomplete, fewer runtime surprises, and more confidence when refactoring.