In listing the differences between type
and interface
within Typescript, it only mentions the different syntax.
Now up till now, I've been using type
notation as my default, however I've had some situation where I want to define more specific types based on a "Base" type, which I'm not able to achieve using type
, but apparently am able to achieve using interface
.
Given the following code, would there be a way with using type
to maintain type-safety as appears to happen with interface
or is this an actual difference between type
and interface
that I can't find in the documentation?
type Vehicle = {
wheelCount: number;
size: 'small' | 'medium' | 'large'
}
type ExtendUsingType = Vehicle & {
// This can be any value, doesn't even have to be of type string
size: 'no-type-safety';
}
interface ExtendUsingInterface extends Vehicle {
// it has to be either 'small', 'medium' or 'large'
size: 'this-will-error';
}
CodePudding user response:
There is a different semantic meaning between intersection and extends.
An intersection means that for a value to be part of the intersection, it must satisfy both types (in terms of sets, the value must be in each of the sets defined by the two intersecting types). This might not always be possible. You could have types such as in your case that generate types that are uninhabitable by any value, basically resolving to never
which is the empty set (and ExtendUsingType
is resolved as never
, although not all uninhabitable types are eagerly collapsed to never
)
With extends
you are saying that this new type, must be a subtype of the type you are extending. In terms of sets, the new interface must be a subset of the set defined by the base type. This means that you must satisfy this constraint of being a subset more rigorously, and will thus get more errors.
You should generally prefer extends
to intersections. This both because you get more of a guarantee that the resulting type is indeed a subtype of the type you are extending, but also because you get better compiler performance (intersections have more complicated semantics and perform worse when type checked)
Intersections still have their uses, sometimes exactly because of the more relaxed checks (as mentioned in the docs). Branded types and defining a type with an index signature incompatible with some of the properties are both things that can only be done with intersections.
CodePudding user response:
type
s can do things that interface
s can not, and that includes &
ing together incompatible types. But note that while typescript won't consider this an error when you define the type, ExtendUsingType
is actually the type never
, so it will be impossible to create a variable that uses that type. As a result, you will actually see errors with ExtendUsingType
, you'll just see them where you try to use the type, not where you define it.