-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
230: Rest Parameters and Rest Arguments Typing
How do I type a function that takes an unknown number of arguments?
In JavaScript, you're probably used to the ...args syntax. In TypeScript, the most important thing to realize is that the "rest" parameter is always treated as an array. So, if you want a function to accept any number of numbers, you don't type the rest parameter as a number; you type it as number[].
I usually run into this when building utility functions, like a calculator that needs to sum up a list of prices. Here is how you'd handle that:
function sumAll(...prices: number[]): number {
return prices.reduce((acc, current) => acc + current, 0);
}
sumAll(10, 20); // Works
sumAll(10, 20, 30, 40); // Also works
sumAll(10, "20"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
One quick tip: the rest parameter must be the last parameter in the function signature. You can't have anything coming after it, otherwise TypeScript (and JavaScript) will throw a fit.
What if I need one specific argument first, followed by a bunch of others?
This is a very common pattern. You might have a "header" or "config" argument, and then a variable list of items to process. You just define your required parameters first, and put the rest parameter at the end. TypeScript is smart enough to know that the first argument belongs to the first named parameter, and everything else gets bundled into the array.
Take a look at this example where I'm building a simple logger that takes a category and then a variable number of messages:
function logEvent(category: string, ...messages: string[]) {
console.log(`[${category}]`);
messages.forEach(msg => console.log(`- ${msg}`));
}
logEvent("Auth", "User logged in", "Token refreshed", "IP verified");
// category = "Auth"
// messages = ["User logged in", "Token refreshed", "IP verified"]
I personally find this much cleaner than forcing the caller to wrap their arguments in an array manually.
Can I restrict the rest parameters to a specific sequence or a limited number of types?
Standard arrays (like string[]) are great for "any number of the same type," but sometimes you need more precision. If you want the rest parameters to follow a specific pattern, you can use a Tuple with a rest element.
Let's say you're writing a function to update a database record. You want the first argument to be the ID (number), the second to be the field name (string), and then any number of values to update (which could be anything). You can do this:
type UpdateArgs = [id: number, field: string, ...values: any[]];
function updateRecord(...args: UpdateArgs) {
const [id, field, ...rest] = args;
console.log(`Updating record ${id} in field ${field} with values: ${rest}`);
}
updateRecord(101, "profile", "John", "Doe", "Developer"); // Valid
updateRecord("101", "profile"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
By using a tuple as the type for the rest parameter, you've essentially created a "schema" for your function arguments. It's a powerful way to keep your API strict while still remaining flexible.
📋 Practical Task
Exercise: Building a Flexible Expense Report Generator
You are building a financial tool that generates expense reports. You need to create a function called generateReport that accepts the following:
- A
reportName(string) as the first argument. - A
currency(string) as the second argument. - A variable number of
amounts(numbers) as the rest parameters.
Requirements:
- Type the function so that it rejects any non-number values in the
amountssection. - The function should return a string in this format:
"Report: [name] | Total: [sum] [currency]". - Use the
.reduce()method to calculate the sum of the amounts.
Starter Code:
function generateReport(reportName: string, currency: string, ...amounts: number[]) {
// Your code here
}
// Test cases
console.log(generateReport("Travel", "USD", 100, 50, 25)); // Should print: "Report: Travel | Total: 175 USD"
console.log(generateReport("Office", "EUR", 10, 20)); // Should print: "Report: Office | Total: 30 EUR"
There are no comments for now.