What are types in TypeScript? This chapter describes two perspectives that help with understanding them. Both are useful; they complement each other.
The following two questions are important for understanding how types work and need to be answered from each of the two perspectives.
What does it mean for arg to have the type MyType?
function myFunc(arg: MyType): void {}
How is UnionType derived from Type1 and Type2?
type UnionType = Type1 | Type2;
From this perspective, we are interested in values and a type is a set of values:
myFunc() if it is included in MyType.
UnionType (a set) is defined as the set-theoretic union of Type1 and Type2.
From this perspective:
The most important type relationship is assignment compatibility: Can a location whose type is Src be assigned to a location whose type is Trg? The answer is yes if:
Src and Trg are identical types.
Src or Trg is the type any.
Src is a string literal type and Trg is the primitive type string.
Src is a union type and each constituent type of Src is assignable to Trg.
Src is an intersection type and at least one constituent type of Src is assignable to Trg.
Trg is a union type and Src is assignable to at least one constituent type of Trg.
Trg is an intersection type and Src is assignable to each constituent type of Trg.
Let’s consider the questions:
arg having type MyType means that we can only pass a value to myFunc() whose type is assignable to MyType.
UnionType is defined by the relationships it has with other types. Above, we have seen two rules for union types.
One of the responsibilities of a static type system is to determine if two static types are compatible – e.g.:
Src of an actual parameter (e.g., provided via a function call)
Trg of the corresponding formal parameter (e.g., specified as part of a function definition)
The type system needs to check if Src is assignable to Trg. Two approaches for this check are (roughly):
In a nominal or nominative type system, two static types are equal if they have the same identity (“name”). Src is only assignable to Trg if they are equal or if a relationship between them was specified explicitly – e.g., an inheritance relationship (extends).
In a structural type system, a type Src is assignable to a type Trg if Trg has a structure that can receive what’s in Src — e.g.: For each field Src.F, there must be a field Trg.F such that Src.F is assignable to Trg.F.
The following code produces a type error in the last line with a nominal type system, but is legal with TypeScript’s structural type system because class A and class B have the same structure:
class A {
typeName = 'A';
}
class B {
typeName = 'B';
}
const someVariable: A = new B();
TypeScript’s interfaces also work structurally – they don’t have to be implemented in order to match:
interface HasTypeName {
typeName: string;
}
const hasTypeName: HasTypeName = new A(); // OK
TypeScript Language Specification 1.8: TypeScript originally had a formal language specification but it was discontinued after TypeScript 1.8 (which came out in 2016). It has since been removed from TypeScript’s repositories, but a PDF file can still be downloaded from an old commit. Especially helpful: section “3.11 Type Relationships”.