Making Generics Types Optional - Generics Fundamentals in Typescript
We continue exploring Typescript generics, by looking at making generic types optional by providing default or fallback types when none is provided or inferred.
In the last two issues, we started exploring generics, where we looked at the fundamentals of generics and how to create generics constraints. I believe this has given you a good foundation for working with generics in Typescript.
You can find the last two issues below:
In this issue, I wanted to explore how we can make a generic Type optional by providing a default for the Type variable, that can be used when no Type is passed in and Typescript cannot infer the Type. Before we can look at generics, letโs draw some parallels with functions and how defaults work values for functional arguments.
Letโs take the following simple example.
function doSomething(input: string) {
// do something
}
In the above example, the function input
parameter is required. In some cases, we want to provide a fallback value, that will be used if the function is called without a value.
For functions, the syntax to achieve this is the one shown below:
In the above example, we are simply telling Javascript that if the input
parameter isnโt provided or undefined, use default-value
as the value for the variable. This makes the input string optional, and not required.
While Typescript will type-check the functionโs input
parameter if provided an incorrect Type or if we simply donโt provide it, it also means that we need to provide the input every time we call the function.
By providing a default value, we made the parameter optional, but this also means we have also expanded the types from being just string to string and undefined and to consume it, we might need to narrow the type. You can learn more about type narrowing here.
However, by providing a default value, it means we donโt need to provide it, so we donโt have to do the extra step of checking if the input is string or undefined, avoiding the extra step for narrowing types, as we already have a fallback value, when undefined.
If you like my content and want to support my work, please consider supporting me (you can buy me a double latte, the juice that powers my creativity and determination โ๏ธ) through Github Sponsors.
What does this have to do with generics?
Default Types for generics types work similarly. We can achieve the same thing we did with function parameters with Generics, where we can provide a fallback type for the type variable if not provided and cannot be inferred.
A type variable is a special kind of variable that works on types rather than values.
Letโs look at an example. Letโs create a simple utility type. Utility types utilize generics to help you do type transformation. Typescript has some in-built utility types that you can learn more about here.
Our utility type will take two types (that are key-value objects) and merge their properties into a single Type, with the properties from both Types.
Here is an example of what our implementation looks like:
To use our utility type, we just need to provide two object types and we get a single object with properties from both types that were passed in, as shown below:
You can find the above code here.
Our example above doesnโt do much, but letโs say that in the future the requirements of our project change and we want to expand it so that we can merge two or three types.
The problem is that this would work with merging 3 Types, but to work with merging two Types, we would need to provide an empty object Type, as shown below:
Wouldnโt be nice if we could skip that part, and just provide our two types? And this is where Generic type variable defaults come to the rescue.
Just like function parameters, we can provide a default Type for our Type variable and essence making them optional, as shown below:
And now, our MergeObjectTypes
utility type can accept either two or three Types and merge their properties into a single Type.
And we can go crazy now and add more type variables to merge 4, 5, or 10 even object properties while ensuring our utility types work with 2 to 10 types, and anywhere in between.
If you are interested, see if you can modify the above example to work with 5 types.
Rules for Default Types
Just like function parameters, there are a few rules to keep in mind for generic default Types.
The first and most obvious one is that when you provide a default value, a Type variable is deemed as optional.
You are required to provide the required type variables.
Remember, Type inference in most cases can help you, so you donโt provide the required Type variables.
If you donโt provide an optional Type variable, then the default type is used, unless Typescript can infer a different type based on usage.
And just for functions, required type variables come first, followed by optional type variables.
If you are using a generics constraint, the default type must satisfy that constraint.
For classes and interfaces, when extending either a class or an interface, you can introduce a default for an existing Type parameter and you may also introduce a new Typer variable, as long as itโs optional (has a type variable).
Conclusion
In this issue, we looked at making generics Type variables optional by providing a default or fallback type. We drew parallels between Generics default types with functions parameters default values and how they behave almost the same, but with the key difference being one applies to variables while the other one applies to types.
To demonstrate how useful default generics types can be, we took a look at an example where we can create a custom utility type that we can use to merge properties for an object type, for either two or three types, and how we can utilize defaults to make this work seamless for either of the cases.
That's it for this issue and thank you for getting this far. If you enjoyed this article and would like to support my work, please share and like this issue and consider sharing All Things Typescript with friends and colleagues.
Did you know you can also hire me to coach your team and help them level up their Typescript game? If this sounds interesting to you, please consider getting in touch and we can work out something.
And until next time, please keep on learning.