-
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
188: Parameters<Type> and ConstructorParameters<Type>
Imagine you're tasked with building a specialized adapter for a piece of hardware. You don't have the original blueprints for the machine, but you have the machine itself. To make your adapter work, you need to know exactly what plugs into the machine—the number of pins, the voltage of each, and the order they go in. Instead of guessing or reading a 500-page manual, you use a "probe" that scans the machine's input port and gives you a precise list of what's required.
In TypeScript, Parameters<T> and ConstructorParameters<T> are those probes. They allow you to extract the type definitions of a function's arguments or a class constructor's arguments without having to manually define those types in a separate interface or type alias.
Peeking inside the function signature
Let's say you have a function that handles user registration. In a real app, this function might be defined in a library or a different module that you don't want to keep importing types from just to keep things DRY.
function registerUser(email: string, age: number, newsletter: boolean) {
// complex registration logic here
return { success: true };
}
// Now, imagine we want to create a "wrapper" or a "logger"
// that takes the exact same arguments as registerUser.
type RegisterArgs = Parameters<typeof registerUser>;
Here is where the magic happens. RegisterArgs isn't just a generic list; it's a tuple. If you hover over it in your editor, you'll see it's exactly [email: string, age: number, newsletter: boolean]. I love this because if I ever change the registerUser function to add a username field, RegisterArgs updates automatically. I don't have to hunt through my codebase to update five different interfaces.
You can then use this tuple to ensure another function stays in sync:
function logRegistration(args: Parameters<typeof registerUser>) {
console.log("Attempting registration with:", args);
}
// This works perfectly
logRegistration("dev@example.com", 30, true);
Handling classes with ConstructorParameters
The logic for classes is almost identical, but since classes are instantiated using the new keyword, we use a specific utility called ConstructorParameters<T>. This targets the constructor specifically, rather than the methods inside the class.
Consider a DatabaseConnection class. It might require a host, a port, and a timeout.
class DatabaseConnection {
constructor(public host: string, public port: number, public timeout: number = 5000) {
console.log(`Connecting to ${host}:${port}...`);
}
}
// We want to extract the types needed to instantiate this class
type DbConfig = ConstructorParameters<typeof DatabaseConnection>;
// DbConfig is now [host: string, port: number, timeout: number]
I've found this incredibly useful when building "Factory" patterns. If you have a function that creates instances of different classes, you can use this to ensure the factory is passing the correct arguments to the constructor without hard-coding the types for every single class you support.
Why we do this instead of using Interfaces
You might be wondering, "Why not just define a UserRegistrationRequest interface and use that in the function signature?"
In a perfect world, you would. But in the real world, you often deal with third-party libraries where the authors didn't export the argument types, or you're working in a massive codebase where changing one interface triggers a cascade of 50 type errors. Using these utility types lets you derive the truth directly from the implementation. It makes your code more resilient to change because the implementation is the single source of truth.
📋 Practical Task
Building a Generic API Request Interceptor
You are building a logging system for an API client. You have a function called fetchUserData that takes a userId (string), a token (string), and an optional includePrivateData (boolean).
Your task is to create a function called interceptRequest. This function must accept a "callback" (which will be fetchUserData) and the arguments required to call that function. To ensure type safety, you must use Parameters<T> to type the arguments of the interceptRequest function so that it dynamically matches whatever function is passed into it.
Requirements:
- Define the
fetchUserDatafunction. - Implement
interceptRequestusing a generic typeT extends (...args: any[]) => any. - Use
Parameters<T>to ensure theargsparameter ofinterceptRequestmatches the signature of the provided callback. - The
interceptRequestfunction should log "Intercepting request..." and then execute the callback with the provided arguments.
There are no comments for now.