Types and Subtypes Relationship in Typescript
In this issue, we explore the relationship between types and their subtypes in Typescript.
One important mental model to form in Typescript is the relationship between types and their subtypes. Typescript is based on structural sub-typing, where when type-checking whether one type can be assigned to another, it checks on the members/properties present, and if all members are present, it allows the assignment.
This is unlike nominal typing, where type compatibility and equivalence are based on the type declaration. For instance, in the eyes of Typescript, the two types below are the same:
You can learn more about structural typing here.
I covered this a while ago, and I wanted to explore sub-types in Typescript in this issue. Unlike the above example, where all properties are the same across the two types, one Type has more members/properties than the other, i.e., is a sub-type of the other. Due to structural typing, the two Types have a relationship, which we will explore in this issue.
We are going to start with looking at an examples:
Take the following string literal type.
type One = "One";
The one One
literal type is a subtype of string and can be used in any place where the input of type one
(keep in mind about structural typing) or a string is required, as shown below:
However, if we used a string instead of a variable of type One
, where One
is required, then it would throw an error:
This means that the type capability between the One
type and string is one way; only One
can be assigned to the string and not vice versa.
“A sub-type can be assigned to a variable of a type that the sub-type is derived from, but not vice versa.”
It is important to understand that the relationship between a Type and its subtype is hierarchical, where a subtype is always narrower than the Type.
Understanding this is important, as it allows you to form a better mental model of how the Typescript Type system works, especially type compatibility and structural typing in Typescript.
Another important reason to understand this is that it allows you to make your type more precise and narrower. Narrower types lead to increased type safety. For instance, a string literal type is always more precise than a string, and just because a function requires a string as an input, it doesn’t mean your variable needs to be a string; instead, you can prefer more narrow string literal types.
A narrower version of a string is always better than a string, as a string could be anything from a single character to a whole book.
Conclusion
In this issue of All Things Typescript, we looked at subtypes in Typescript. Type compatibility in Typescript is based on structural sub-typing, where related types are compared solely by their members (the content of a type).
In this issue, we looked at subtypes in Typescript, how they relate to their parents’ type, and how assigning subtypes to parent types is possible, while vice versa isn’t.
That's it for this issue. 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 hire me to coach your team and help them improve their Typescript skills? If this interests you, please contact me to work something out.
And until next time, please keep on learning.