This TypeScript error occurs when a function's implementation signature doesn't match its declared overload signatures. Overloads allow multiple call signatures for the same function, but the actual implementation must be compatible with all overloads. The error ensures type safety by preventing implementations that can't handle all the declared parameter/return type combinations.
This TypeScript error (TS2394) occurs when you declare function overloads (multiple call signatures) but the actual implementation signature is not compatible with all of them. Function overloads in TypeScript allow you to define multiple ways a function can be called with different parameter types and return types. However, the implementation (the actual function body) must be able to handle all the declared overload cases. The implementation signature must be a superset that can accept parameters from any overload and return a type assignable to all overload return types. This error prevents runtime type mismatches by ensuring the implementation actually satisfies all the declared type contracts.
Function overloads in TypeScript consist of multiple call signatures followed by one implementation. The implementation must be compatible with all overloads.
// Overload signatures (declarations)
function process(input: string): string;
function process(input: number): number;
function process(input: boolean): boolean;
// Implementation signature (actual function)
function process(input: string | number | boolean): string | number | boolean {
if (typeof input === "string") return input.toUpperCase();
if (typeof input === "number") return input * 2;
return !input;
}The implementation uses a union type for the parameter and return type to handle all three overload cases.
The implementation parameter type must be a supertype (more general) of all overload parameter types. Use union types to combine all possible parameter types.
// ERROR: Implementation doesn't handle number case
function parse(value: string): Date;
function parse(value: number): Date;
function parse(value: string): Date { // Missing number parameter type
return new Date(value);
}
// CORRECT: Use union type for implementation
function parse(value: string): Date;
function parse(value: number): Date;
function parse(value: string | number): Date {
return new Date(value);
}
// Also correct: Using any or unknown (less type-safe)
function parse(value: string): Date;
function parse(value: number): Date;
function parse(value: any): Date {
return new Date(value);
}The implementation return type must be assignable to (compatible with) all overload return types. Usually this means returning a union type or a common supertype.
// ERROR: Implementation returns string|number, but overload requires string for boolean input
function transform(input: string): string;
function transform(input: number): number;
function transform(input: boolean): string;
function transform(input: string | number | boolean): string | number {
if (typeof input === "string") return input.toUpperCase();
if (typeof input === "number") return input * 2;
return "boolean: " + input; // This satisfies boolean → string overload
}
// CORRECT: Implementation returns string|number which covers all cases
function transform(input: string): string;
function transform(input: number): number;
function transform(input: boolean): string;
function transform(input: string | number | boolean): string | number {
if (typeof input === "string") return input.toUpperCase();
if (typeof input === "number") return input * 2;
return "boolean: " + input;
}Optional parameters and rest parameters must be compatible across all overloads. The implementation should handle the most general case.
// ERROR: Implementation missing optional second parameter
function greet(name: string): string;
function greet(name: string, title?: string): string;
function greet(name: string): string { // Missing title parameter
return `Hello ${name}`;
}
// CORRECT: Implementation includes optional parameter
function greet(name: string): string;
function greet(name: string, title?: string): string;
function greet(name: string, title?: string): string {
if (title) {
return `Hello ${title} ${name}`;
}
return `Hello ${name}`;
}
// With rest parameters:
function sum(...numbers: number[]): number;
function sum(a: number, b: number): number;
function sum(...args: number[]): number {
return args.reduce((total, num) => total + num, 0);
}When using generics with overloads, ensure type parameters are compatible. The implementation often needs to use the most general constraint.
// ERROR: Implementation T doesn't match overload constraints
function identity<T extends string>(value: T): T;
function identity<T extends number>(value: T): T;
function identity<T>(value: T): T { // T is too general
return value;
}
// CORRECT: Use union type constraint
function identity<T extends string>(value: T): T;
function identity<T extends number>(value: T): T;
function identity<T extends string | number>(value: T): T {
return value;
}
// Alternative: Separate implementations
function identity(value: string): string;
function identity(value: number): number;
function identity(value: string | number): string | number {
return value;
}Within the implementation, use type guards to narrow types and handle different overload cases appropriately.
function processValue(input: string): string[];
function processValue(input: number): number[];
function processValue(input: string | number): string[] | number[] {
// Type guard for string case
if (typeof input === "string") {
return input.split(","); // Returns string[] for string overload
}
// Type guard for number case
if (typeof input === "number") {
return [input, input * 2, input * 3]; // Returns number[] for number overload
}
// Fallback that satisfies the union return type
throw new Error("Unexpected input type");
}
// Using type predicates for custom types
interface Cat { meow(): void; }
interface Dog { bark(): void; }
function isCat(pet: Cat | Dog): pet is Cat {
return "meow" in pet;
}
function getSound(pet: Cat): "meow";
function getSound(pet: Dog): "bark";
function getSound(pet: Cat | Dog): "meow" | "bark" {
if (isCat(pet)) {
return "meow";
}
return "bark";
}Sometimes the best fix is to simplify the overload structure. Consider using union types or conditional types instead of multiple overloads.
// Complex overloads that are hard to implement correctly
function complex(input: string, flag: true): string;
function complex(input: string, flag: false): number;
function complex(input: number, flag: boolean): boolean;
function complex(input: string | number, flag: boolean): string | number | boolean {
// Complex implementation needed
if (typeof input === "string") {
return flag ? input.toUpperCase() : input.length;
}
return flag;
}
// Simplified using conditional return type
function simplified<T extends string | number>(
input: T,
flag: boolean
): T extends string ? (flag extends true ? string : number) : boolean {
// Implementation with type assertions
if (typeof input === "string") {
return (flag ? input.toUpperCase() : input.length) as any;
}
return flag as any;
}
// Even simpler: Use separate functions
function processString(input: string, flag: true): string;
function processString(input: string, flag: false): number;
function processString(input: string, flag: boolean): string | number {
return flag ? input.toUpperCase() : input.length;
}
function processNumber(input: number, flag: boolean): boolean {
return flag;
}Review your code for these common issues that cause overload compatibility errors:
// ANTI-PATTERN: Implementation more specific than overloads
function example(x: string): any;
function example(x: any): string { // ERROR: Returns string but overload says any
return x.toString();
}
// FIX: Make implementation match overload
function example(x: string): any;
function example(x: any): any {
return x.toString();
}
// ANTI-PATTERN: Parameter count mismatch
function create(length: number): number[];
function create(start: number, end: number): number[];
function create(arg1: number): number[] { // ERROR: Missing second parameter
return new Array(arg1).fill(0);
}
// FIX: Add optional second parameter
function create(length: number): number[];
function create(start: number, end: number): number[];
function create(arg1: number, arg2?: number): number[] {
if (arg2 !== undefined) {
return Array.from({length: arg2 - arg1 + 1}, (_, i) => arg1 + i);
}
return new Array(arg1).fill(0);
}
// ANTI-PATTERN: Incompatible generic constraints
function convert<T extends string>(value: T): Uppercase<T>;
function convert<T extends number>(value: T): string;
function convert(value: string | number): string { // ERROR: Missing generic
return value.toString().toUpperCase();
}
// FIX: Remove generics or adjust implementation
function convert(value: string): string;
function convert(value: number): string;
function convert(value: string | number): string {
return value.toString().toUpperCase();
}TypeScript function overloads are a design-time construct - they don't exist at runtime. The compiler uses them for type checking but erases them during compilation to JavaScript. The implementation signature is the only one that appears in the output JavaScript. When checking overload compatibility, TypeScript uses structural type compatibility rules. Parameters are checked contravariantly (when strictFunctionTypes is enabled), meaning the implementation can accept more general parameter types than the overloads. Return types are checked covariantly, meaning the implementation must return more specific types (subtypes) of the overload return types. The any type is both a supertype and subtype of all types, which is why using any in the implementation often "fixes" overload errors but reduces type safety. For complex overload scenarios, consider using conditional types (T extends U ? X : Y) or function overloads with generic constraints. Remember that method overloads in classes follow the same rules. The override keyword (TypeScript 4.3+) can help catch overload compatibility issues in class hierarchies. When migrating from JavaScript, you might need to add explicit type annotations to overload implementations that were previously inferred.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript