Parameters, but only sometimes
16 August 2024
Updated: 24 August 2024
Ran into this question today and I thought it would be a nice little example to document:
I have the following function doWork
that is generic:
1function doWork<T>(data?: T): void {2 console.log(data)3}
This function can be called in any of the following ways:
1// Works2doWork<string>('hello') // T is string, data is string3doWork() // T is undefined, data is undefined4doWork(undefined) // T is undefined, data is undefined;5
6doWork<string>() // T is string, data is undefined
In the above usage, we want to make it so that users of this function need to provide the data
parameter when calling the function when the type is provided. Now, a simple solution could be to define our function as follows:
1function doWork<T>(data: T): void
The problem is that it’s a bit ugly for cases where we want to allow T
as undefined
, because you now have to do this:
1doWork(undefined)
So the issue here is that we only want to make the data
parameter required when T
is not undefined
, there’s a lot of funny stuff you can try using generics and other weird typescript notation, but simply defining the necessary overloads for our method works:
1/**2 * This specifically handles the case where T is undefined3 */4function doWork<T extends undefined>(): void5/**6 * The data param will be required since T is not undefined here7 */8function doWork<T>(data: T): void9/**10 * This provideds an implementation that is the same as before while providing a11 * better interface for function users12 */13function doWork<T>(data?: T): void {14 console.log(data)15}
Now, having defined those function overloads, we can use the function and it works as expected:
1// Works2doWork<string>('hello') // T is string, data is required3doWork() // T is undefined, data parameter is not required4doWork(undefined) // T is undefined, specifying data is still okay5
6// Error: Type 'string' does not satisfy the constraint 'undefined'7doWork<string>() // this overload is not valid
The added benefit of this method is that we can also write the doc comments for each implementation separately which can be a nice way for us to give additional context to consumers. It’s kind of like having two specialized functions without the overhead of having to implement them independently