Fix TS2554: Expected N Arguments But Got M (TS 2026)

TS2554: Expected N arguments, but got M fires when you call a function with the wrong number of arguments. Very common when refactoring, passing callbacks to array methods, and adding optional parameters. Fix by matching the call to the signature, making arguments optional, or using rest parameters. Here is the complete 2026 fix guide.

Why TS2554 happens

TypeScript enforces function arity strictly. If a function expects 2 required arguments and you pass 1 or 3, TS2554 fires. Optional parameters and rest parameters relax this. Fix by aligning your call with the signature, updating the signature to match reality, or introducing optional/rest parameters intentionally.

Fix 1: too few arguments

function greet(name: string, greeting: string) {
  return `${greeting}, ${name}`;
}

// WRONG
greet("Ana");
// TS2554: Expected 2 arguments, but got 1.

// FIX 1: provide the missing argument
greet("Ana", "Hello");

// FIX 2: make greeting optional with default
function greet(name: string, greeting: string = "Hello") {
  return `${greeting}, ${name}`;
}
greet("Ana");   // works

Fix 2: too many arguments

function log(msg: string) {
  console.log(msg);
}

// WRONG
log("hi", "world");
// TS2554: Expected 1 argument, but got 2.

// FIX 1: drop the extra argument
log("hi");

// FIX 2: use rest params if you truly need variadic
function log(...msgs: string[]) {
  console.log(...msgs);
}
log("hi", "world");   // works

Fix 3: passing callback to Array.forEach

// forEach passes (value, index, array) to your callback
const nums = [10, 20, 30];

// This works - your callback ignores the extra args
nums.forEach(n => console.log(n));

// But this pattern breaks
function loggerNoArgs() { console.log("hit"); }
nums.forEach(loggerNoArgs);
// TS2554 if strict about signature match

// FIX: wrap or update signature
nums.forEach(() => loggerNoArgs());

// or
function logger(_v: number, _i: number, _arr: number[]) {
  console.log("hit");
}
nums.forEach(logger);

Fix 4: React custom hook signature change

// Custom hook with required arg
function useUser(userId: string) {
  return fetch(`/api/users/${userId}`).then(r => r.json());
}

// WRONG when calling from a component before ID is known
const user = useUser();
// TS2554: Expected 1 argument, but got 0.

// FIX 1: make userId optional and handle null case
function useUser(userId?: string) {
  if (!userId) return Promise.resolve(null);
  return fetch(`/api/users/${userId}`).then(r => r.json());
}

// FIX 2: pass the missing value
const user = useUser(session?.userId ?? "");

Debugging checklist

  • Read the exact number: TS2554 tells you expected N vs actual M. Fix the count.
  • Check the function definition to see which params are required vs optional.
  • For third-party functions, upgrade @types/* to match the runtime library version. Signature changes cause TS2554 after upgrades.
  • For array methods, remember every callback receives value, index, array in that order.

Common mistakes when fixing TS2554

  • Adding ? to make required params optional. This shifts the check downstream and often causes TS2531 later.
  • Passing undefined to satisfy the argument count. If the param type is not | undefined, TypeScript rejects this. Fix the actual signature or the call.
  • Using rest params to swallow unexpected args silently. Loses type safety on the extras.
  • Fixing at call site instead of function definition. If every caller passes the same default, put the default on the function.

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.

  1. 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.
  2. Check strictNullChecks and strict mode. Many errors trace back to strict: true in tsconfig.json. If you inherited a codebase with strict mode on, expect more errors than in loose mode.
  3. Look at inferred types with hover. VS Code shows inferred types on hover. Compare what TypeScript sees vs what you assumed.
  4. 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.
  5. Isolate the failing line in a minimal reproducer. A standalone .ts file 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: true enables 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: true speeds up compilation but hides type mismatches in dependencies. Fine for CI but keep it off during development.
  • Path aliases and moduleResolution mismatches. The paths config 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 any without a good reason. unknown is 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-type or tsd let 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.

Frequently Asked Questions

Why is JavaScript looser about arity?

JavaScript ignores extra arguments and gives you undefined for missing ones. TypeScript is stricter to catch bugs at compile time that JavaScript would surface at runtime.

Optional param or default value?

Default value if there is a sensible fallback. Optional if absence itself is meaningful. Default is often cleaner because it eliminates the undefined case downstream.

Can I overload a function to accept different arg counts?

Yes with function overload signatures. Declare each valid signature above the implementation. Often a single generic function is cleaner though.

Does TS2554 apply to spread arguments?

Yes. Spread arrays must match the fixed arity of the target. Use rest params on the target if you truly want variadic.

Does refactoring cause TS2554 often?

Yes and this is a feature. When you add or remove a parameter, TypeScript points to every call site that needs updating. Do not silence, update the calls.

Leave a Comment