Home > Blockchain >  Type safety difference between type and interface when extending
Type safety difference between type and interface when extending

Time:02-26

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:

types can do things that interfaces 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.

  • Related