How to use JSDoc to add type contraints to generic function arguments
Today I learnt …
The other day, while working on a Typescript/Javascript codebase, I found myself writing a function that took a single argument. That argument might be one of two types, and the function would return an array of the same type. For example, if the function was passed a string, it would return an array of strings. But if the function was passed a number, it would return an array of numbers. I had most of the function written; all that was missing was the type information.
Adding the necessary types would have been no problem in Typescript, where a sprinkling of generics would provide the information needed. If we name the function foo
and simplify the code it contained, we end up with something like this:
;
With this code, when I pass a string Typescript would know the result is guaranteed to be an array of strings. And when I pass a number, Typescript would know the result would be an array of numbers.
; // result1 is a string[]
; // result2 is a number[]
The problem I had was that the function was in a Javascript file, not a Typescript file, and so I needed to type the function using Typescript’s flavour of JSDoc. While this syntax has a lot in common with Typescript, it’s all added above the function in a comment rather than being inline. How exactly should I structure that comment?
I completely failed to find the answer online and instead worked it out by trial and error. Delightfully, the answer ended up being more succinct than the Typescript version:
/**
* @template {string | number} T
*
* @param {T} bar
* @returns {T extends string ? string[] : number[]}
*/
The constraint on the Typescript function — <T extends string | number>
— was replaced by using a @template
tag, the bar
argument was typed using @param
, and the return type was just declared directly rather than needing to use a defined type. That in turn removed the need to coerce the return values (return ... as Result<T>
). All in all, quite an elegant solution.