Quick Answer
This error means wrong number of arguments passed to function. Use optional parameters, default values, or function overloads to handle variable argument scenarios.
Understanding the Issue
The "Expected X arguments, but got Y" error is TypeScript's strict function signature enforcement in action. This error occurs when the number of arguments passed to a function doesn't match the number of required parameters in the function definition. TypeScript treats all parameters as required by default unless explicitly marked as optional with ? or given default values. This error helps catch common programming mistakes like missing arguments, extra arguments, or mismatched function signatures. Understanding parameter patterns, optional parameters, rest parameters, and function overloading is essential for flexible yet type-safe function design.
The Problem
This code demonstrates the issue:
Typescript
Error
// Problem 1: Missing required arguments
function calculateArea(width: number, height: number): number {
return width * height;
}
calculateArea(10); // Error: Expected 2 arguments, but got 1
// Problem 2: Too many arguments passed
function greet(name: string): string {
return `Hello, ${name}!`;
}
greet("Alice", "Bob"); // Error: Expected 1 arguments, but got 2
The Solution
Here's the corrected code:
Typescript
Fixed
// Solution 1: Use optional parameters and default values
// Optional parameters with ?
function calculateArea(width: number, height?: number): number {
return width * (height ?? width); // Default to square if height not provided
}
console.log(calculateArea(10)); // OK: 100 (square)
console.log(calculateArea(10, 5)); // OK: 50 (rectangle)
// Default parameter values
function greetUser(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
console.log(greetUser("Alice")); // OK: "Hello, Alice!"
console.log(greetUser("Bob", "Hi")); // OK: "Hi, Bob!"
// Multiple optional parameters
function createUser(
name: string,
age?: number,
email?: string,
isActive: boolean = true
): object {
return {
name,
age: age ?? 0,
email: email ?? `${name.toLowerCase()}@example.com`,
isActive
};
}
console.log(createUser("John")); // All optionals use defaults
console.log(createUser("Jane", 25)); // Age provided
console.log(createUser("Bob", 30, "bob@test.com")); // Age and email provided
console.log(createUser("Alice", 28, "alice@test.com", false)); // All parameters
// Solution 2: Use rest parameters and function overloads
// Rest parameters for variable arguments
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum()); // OK: 0
console.log(sum(1)); // OK: 1
console.log(sum(1, 2, 3, 4, 5)); // OK: 15
// Function overloads for different argument patterns
function format(value: string): string;
function format(value: number, decimals?: number): string;
function format(value: Date, format?: string): string;
function format(value: string | number | Date, option?: number | string): string {
if (typeof value === "string") {
return value.toUpperCase();
} else if (typeof value === "number") {
const decimals = typeof option === "number" ? option : 2;
return value.toFixed(decimals);
} else if (value instanceof Date) {
const dateFormat = typeof option === "string" ? option : "ISO";
return dateFormat === "ISO" ? value.toISOString() : value.toLocaleDateString();
}
return String(value);
}
console.log(format("hello")); // "HELLO"
console.log(format(123.456)); // "123.46"
console.log(format(123.456, 1)); // "123.5"
console.log(format(new Date())); // ISO string
console.log(format(new Date(), "US")); // US date format
// Advanced: Generic functions with constraints
interface ApiOptions {
timeout?: number;
retries?: number;
headers?: Record<string, string>;
}
function apiCall<T>(
url: string,
options: ApiOptions = {}
): Promise<T> {
const defaultOptions: Required<ApiOptions> = {
timeout: 5000,
retries: 3,
headers: {}
};
const finalOptions = { ...defaultOptions, ...options };
return fetch(url, {
method: "GET",
headers: finalOptions.headers,
signal: AbortSignal.timeout(finalOptions.timeout)
}).then(response => response.json());
}
// Usage with different argument patterns
apiCall<User[]>("/api/users"); // Just URL
apiCall<User[]>("/api/users", { timeout: 3000 }); // URL + partial options
apiCall<User[]>("/api/users", { retries: 5, headers: { "Authorization": "Bearer token" } }); // Full options
// Complex function with multiple parameter patterns
class Database {
// Method overloads for different query patterns
query(sql: string): Promise<any[]>;
query(sql: string, params: any[]): Promise<any[]>;
query<T>(sql: string, params: any[], returnType: new() => T): Promise<T[]>;
async query<T = any>(
sql: string,
params?: any[],
returnType?: new() => T
): Promise<T[]> {
// Implementation would execute SQL with params
console.log(`Executing: ${sql}`, params || []);
// Mock return based on type
if (returnType) {
return [new returnType()] as T[];
}
return [] as T[];
}
// Builder pattern for complex queries
select(columns: string[] = ["*"]) {
return new QueryBuilder(this, "SELECT", columns);
}
}
class QueryBuilder {
constructor(
private db: Database,
private type: string,
private columns: string[]
) {}
from(table: string) {
// Return new builder with table
return this;
}
where(condition: string, value?: any) {
// Add where clause
return this;
}
execute<T = any>(): Promise<T[]> {
// Build and execute query
return this.db.query("SELECT * FROM table");
}
}
// Usage examples
const db = new Database();
// Different argument patterns work
db.query("SELECT * FROM users");
db.query("SELECT * FROM users WHERE id = ?", [1]);
db.query("SELECT * FROM users WHERE active = ?", [true], User);
// Builder pattern with fluent interface
db.select(["id", "name", "email"])
.from("users")
.where("active = ?", true)
.execute<User>();
// Utility functions with flexible parameters
function log(message: string, level: "info" | "warn" | "error" = "info"): void;
function log(error: Error, context?: string): void;
function log(
messageOrError: string | Error,
levelOrContext?: string
): void {
if (messageOrError instanceof Error) {
console.error(`Error${levelOrContext ? ` in ${levelOrContext}` : ""}: ${messageOrError.message}`);
} else {
const level = levelOrContext as "info" | "warn" | "error" || "info";
console[level](`[${level.toUpperCase()}] ${messageOrError}`);
}
}
// All these calls work correctly
log("Application started");
log("Warning message", "warn");
log(new Error("Something went wrong"));
log(new Error("Database error"), "UserService");
// Interface for function options pattern
interface ProcessOptions {
parallel?: boolean;
timeout?: number;
onProgress?: (completed: number, total: number) => void;
onError?: (error: Error) => void;
}
function processItems<T, R>(
items: T[],
processor: (item: T) => Promise<R>,
options: ProcessOptions = {}
): Promise<R[]> {
const {
parallel = false,
timeout = 30000,
onProgress,
onError
} = options;
// Implementation would process items based on options
console.log(`Processing ${items.length} items`, { parallel, timeout });
return Promise.resolve([] as R[]);
}
// Flexible usage
processItems([1, 2, 3], async (x) => x * 2);
processItems([1, 2, 3], async (x) => x * 2, { parallel: true });
processItems([1, 2, 3], async (x) => x * 2, {
parallel: true,
timeout: 10000,
onProgress: (completed, total) => console.log(`${completed}/${total}`),
onError: (error) => console.error("Processing error:", error)
});
Key Takeaways
Use optional parameters (?) for non-required arguments. Provide default values for common use cases. Implement function overloads for different argument patterns. Use rest parameters (...args) for variable argument counts. Consider options objects for functions with many optional parameters.