Reputation: 422
Ok so I need some advanced typescript type tricks. I have a function that take 2 parameters, but I want the second parameter type to depends on the first parameter type. For exemple if my first parameter is a number, that my second param need to be a string, but if my first parameter is Record, that my second param be a number. I know I could do some function prototype overloading, but I want this to be dynamic, so It would look like something like this:
type Test = [number, string] | [Record<string, any>, number];
foo<Test>(34, "bar") // ok
foo<Test>({a:34}, 56) // ok
foo<Test>(34, 56) // error
foo<Test>({a:34}, "bar") // error
I don't have the advanced knowledge in typescript to figure it out on my own, so I come to all of you guys to guide me, if this is even possible.
Upvotes: 1
Views: 2561
Reputation: 327624
You are allowed to use tuple types for rest parameters, including unions of such tuples. So you can declare foo()
to be a generic function like this:
declare function foo<T extends readonly any[]>(...args: T): void;
which would cause your example to behave exactly as shown when you specify Test
as the T
parameter:
foo<Test>(34, "bar") // ok
foo<Test>({ a: 34 }, 56) // ok
foo<Test>(34, 56) // error
foo<Test>({ a: 34 }, "bar") // error
If you don't need foo()
to be generic then you can hardcode the Test
rest tuple parameter:
declare function bar(...args: Test): void;
bar(34, "bar") // ok
bar({ a: 34 }, 56) // ok
bar(34, 56) // error
bar({ a: 34 }, "bar") // error
Either way will work. TypeScript treats unions-of-rest-tuples as similar to overloads, and even the quickinfo in IntelliSense will display them as overloads:
bar(/* hints */)
// 1/2 bar(args_0: number, args_1: string): void
// 2/2 bar(args_0: Record<string, any>, args_1: number): void
Upvotes: 2
Reputation: 214927
You can define the first parameter to be generic type T
which extends number | Record<string, any>
and then second parameter type to conditionally depend on type T
:
function foo<T extends number | Record<string, any>>(
x: T, y: T extends number ? string : number) {
}
Upvotes: 2