Structural Typing
TypeScript is a Structural Type System. A structural type
system means that when comparing types, TypeScript only
takes into account the members on the type.
This is in contrast to nominal type systems, where you
could create two types but could not assign them to each
other. See example:nominal-typing
For example, these two interfaces are completely
transferrable in a structural type system:
// If we add in a type which structurally contains all of
the members of Ball and Sphere, then it also can be
set to be a ball or sphere.
interface Ball {
diameter: number;
}
interface Sphere {
diameter: number;
}
let ball: Ball = { diameter: 10 };
let sphere: Sphere = { diameter: 20 };
sphere = ball;
ball = sphere;
// Because a ball does not have a length, then it cannot be
assigned to the tube variable. However, all of the members
of Ball are inside tube, and so it can be assigned.
TypeScript is comparing each member in the type against
each other to verify their equality.
A function is an object in JavaScript and it is compared
in a similar fashion. With one useful extra trick around
the params:
interface Tube {
diameter: number;
length: number;
}
let tube: Tube = { diameter: 12, length: 3 };
tube = ball;
ball = tube;
// TypeScript will allow (number) to equal (number, boolean)
in the parameters, but not (number, boolean) -> (number)
TypeScript will discard the boolean in the first assignment
because it's very common for JavaScript code to skip passing
params when they're not needed.
For example the array's forEach's callback has three params,
value, index and the full array - if TypeScript didn't
support discarding parameters, then you would have to
include every option to make the functions match up:
let createBall = (diameter: number) => ({ diameter });
let createSphere = (diameter: number, useInches: boolean) => {
return { diameter: useInches ? diameter * 0.39 : diameter };
};
createSphere = createBall;
createBall = createSphere;
// Return types are treated like objects, and any differences
are compared with the same object equality rules above.
[createBall(1), createBall(2)].forEach((ball, _index, _balls) => {
console.log(ball);
});
// No one needs that.
// Where the first assignment works (they both have diameter)
but the second doesn't (the ball doesn't have a color).
let createRedBall = (diameter: number) => ({ diameter, color: "red" });
createBall = createRedBall;
createRedBall = createBall;