Reputation: 1865
I'm trying to achieve good developer experience for my TypeScript API, but I'm facing unexpected issue. When overloading function like shown below:
/** Comment 1 */
function a(key: 'key1', args: { arg1: number }): string
/** Comment 2 */
function a(key: 'key2', args: { arg2: number }): string
function a(key: string, args) { }
When someone is trying to call that function in an IDE (I tried VSCode and WebStorm)
a('key2', { })
// ^
the IDE suggests object property based on union of second type. (in this case arg1
and arg2
)
I would like It to behave similar to this code:
/** Comment 1 */
function a(key: 'key1'): (args: { arg1: number }) => string
/** Comment 2 */
function a(key: 'key2'): (args: { arg2: number }) => string
function a(key: string, args) { }
where when calling
a('key2')({ })
// ^
only needed properties show up.
Is it possible to get the same behavior in the first case?
Upvotes: 1
Views: 508
Reputation: 754
This is a common problem - for instance in event listener contexts - and I faced it myself recently. Fortunately, there is a simple and elegant solution.
First of all, in your question there are two slightly different formulations of the problem; once, the object you want to type correctly is another argument to a
, and once it is the singular argument to a function that you want to return from a
. The following solution should work for both versions, but I'll present it for the first scenario, which should be more common.
The main trick is to avoid overloads and replace them by a single generic; that also makes handling a
and typing much easier. As far as I can think, this should always be possible in the cases you describe.
Step 1: Define your function signatures/overloads by an interface:
interface Signatures {
key1: {arg1: number};
key2: {arg2: number};
}
Of course, that would be a bit more difficult for more than 2 arguments; but scenarios that are typical for the mentioned problem to arise usually have exactly 2 arguments - one key and one value.
Step 2: Define a single function generic function signature:
function a<K extends keyof Signatures>(key: K, args: Signatures[K]): string {
return 'hello world'; // TODO
}
Then, when you call a('key1',
), args
is automatically inferred to be of type {arg1: number}
.
Upvotes: 2