When you restrict a union to only include certain elements, it is known as subtyping. In the world of TypeScript, declaring A extends B
signifies that type A
is a subtype of type B
. This concept might be confusing at first glance since removing elements from a union actually makes the type more specific, hence making it a subtype. The use of the word "extends" in this context may seem counterintuitive, but that's just how TypeScript works.
Unfortunately, when it comes to narrowing down type aliases using extends
, the functionality is not supported the same way it is for interfaces. Attempting to do so with the syntax below will result in an error:
// Incorrect TypeScript syntax, avoid using:
type MySubUnion extends MyUnionType = 'foo' | 'bar'; // should work
type MySubUnion extends MyUnionType = 'foo' | 'bas'; // should fail
As a workaround, you can create a custom type function called Extends<T, U>
, which will resolve to U
only if U
can be extended by T
:
type Extends<T, U extends T> = U;
With this new function in place, you can modify the invalid code into proper TypeScript syntax like so:
type MySubUnion = Extends<MyUnionType, 'foo' | 'bar'>; // Compiles without errors
And similarly:
type MySubUnion = Extends<MyUnionType, 'foo' | 'bas'>; // Error:
// Type '"bas"' is not assignable to type 'MyUnionType'.
Hoping this explanation helps clarify things for you! Best of luck!