The Error
You're calling a function and TypeScript throws:
No overload matches this call. ts(2769)
What follows is usually a wall of text โ one block per overload, explaining why your arguments failed each one. Annoying to read, but the structure is actually useful once you know how to skim it.
The short version: TypeScript walked through every available signature for that function and your call didn't fit any of them.
Root Cause
Function overloads let you describe a function that behaves differently depending on what you pass in. TypeScript checks your arguments against each overload signature in order. Miss all of them โ ts(2769).
The most common triggers:
- Passing a union type (
string | undefined) where every overload expects juststring - Wrong argument count โ one too many, or one too few
- Mismatched argument types (a callback where a string is expected, for example)
- Third-party library types with strict overloads โ React's
createElement, Node'sfs.readFile, etc. - Your own overloaded function where the implementation signature is too narrow to cover all declared variants
Fix 1 โ Narrow the Type Before Calling
This is the most common cause by far. You have a string | undefined value, but every overload only accepts string. TypeScript can't pick an overload because none of them account for undefined.
// โ Breaks โ name could be undefined
function greet(name: string): string;
function greet(name: string, greeting: string): string;
function greet(name: string, greeting?: string) {
return greeting ? `${greeting}, ${name}` : `Hello, ${name}`;
}
const name: string | undefined = getName();
greet(name); // No overload matches this call. ts(2769)
// โ
Fixed โ narrow first
if (name !== undefined) {
greet(name);
}
// Or collapse the undefined with nullish coalescing
greet(name ?? 'stranger');
Fix 2 โ Check Argument Count and Order
Overloads are position-sensitive. One extra argument, or one in the wrong slot, is enough to fail the match.
// Overloads defined as:
function resize(width: number): void;
function resize(width: number, height: number): void;
// โ Wrong
resize('100px'); // ts(2769) โ string, not number
resize(100, 200, 'cover'); // ts(2769) โ three args, max is two
// โ
Correct
resize(100);
resize(100, 200);
Fix 3 โ Widen Your Implementation Signature
Writing your own overloads? The implementation signature โ the actual function body โ must be broad enough to cover every declared variant. If it's too narrow, TypeScript will reject calls that should be valid.
// โ Broken โ implementation signature is missing the string branch
function process(input: number): number;
function process(input: string): string;
function process(input: number): number | string { // ([]);
setItems([{ id: 1 }]); // works
// โ useRef without a type โ current is typed as null, no .focus()
const ref = useRef(null);
ref.current.focus(); // ts(2769)
// โ
Type the ref properly
const ref = useRef(null);
if (ref.current) ref.current.focus();
Fix 5 โ Node.js fs and Callback Overloads
Node's built-in modules are heavily overloaded. The pattern that breaks most often: forgetting the encoding argument, which forces TypeScript to pick the Buffer overload instead of the string one.
import { readFile } from 'fs';
// โ No encoding โ data is typed as Buffer, TypeScript complains on .toString()
readFile('./config.json', (err, data) => {
console.log(data.toString());
});
// โ
Add 'utf8' โ TypeScript picks the string overload, data is string
readFile('./config.json', 'utf8', (err, data) => {
console.log(data);
});
Fix 6 โ Type Assertion as Last Resort
Strict or incorrect third-party type definitions sometimes leave you stuck even when your call is valid at runtime. A targeted assertion can unblock you without turning off TypeScript entirely.
// Cast the function itself
import { someLibraryFn } from 'some-library';
(someLibraryFn as (arg: string | undefined) => void)(value);
// Or just cast the argument
someLibraryFn(value as string);
Reach for this only when the library types are genuinely wrong. Fix the actual type issue first โ assertions hide bugs rather than solve them.
Reading the Error Output
Don't read the error top-to-bottom. Skip to the last overload listed โ it's usually the one closest to what you intended, and its failure message is the most actionable.
No overload matches this call.
Overload 1 of 2, '(input: number): number', gave the following error.
Argument of type 'string | number' is not assignable to parameter of type 'number'.
Overload 2 of 2, '(input: string): string', gave the following error.
Argument of type 'string | number' is not assignable to parameter of type 'string'.
Clear diagnosis: your argument is string | number but each overload wants exactly one or the other. Narrow with a type guard before calling and this goes away.
Verify the Fix
Skip a full build โ check just what you changed:
# Check a single file
npx tsc --noEmit path/to/your/file.ts
# Or the whole project
npx tsc --noEmit
# In VS Code โ hover over the call site
# Red squiggle gone + hover shows the correct return type = fixed
Prevention
- Don't let union types leak into call sites. If a function returns
string | undefinedand callers always narrow before use, ask whether it should just throw on the missing case instead. - When writing your own overloads โ make the implementation signature as wide as necessary (
string | number | undefined) even if callers only see the narrower declared overloads. - Always supply type arguments to generic functions like
useState,useRef, andArray.fromwhen the initial value doesn't give TypeScript enough to infer from. - Enable strict mode in
tsconfig.json. It surfaces these mismatches during development, not during a 2 AM integration run.

