Ways of categorizing type systems
Now is a good time to take a step back and think about some conceptual aspects of types and type systems. How is TypeScript similar and different from Java and JavaScript?
What is type checking?
As we discussed earlier, type-checking can be thought of as a task that attempts to evaluate the question of compatibility or type equivalence.
“is type
yequivalent to typex? —> “does the type ofyfit within the type ofx?
This question can be asked at a function call
tsTryfunctionfoo (x ) {// ... mystery code ...}//// TYPE CHECKING// -------------// Is `myValue` type-equivalent to// what `foo` wants to receive?foo (myValue )
or an assignment,
ts// is the value y holds type-equivalent to what `x` allows?x = y
or a function return,
tsTryfunctionbar (): string[] {if (Type 'undefined' is not assignable to type 'string[]'.2322Type 'undefined' is not assignable to type 'string[]'.Math .random () > 0.5)return // TYPE CHECKING// -------------// Is `myStrings` type-equivalent to// what `bar` states it will return?return ["a"];}
as well as in some other more exotic situations 1.
Static vs dynamic
Sorting type systems as either static or dynamic has to do with whether type-checking is performed at compile time or not.
TypeScript’s type system is static.
Java, C#, C++ all fit into this category. Keep in mind that inference can still occur in static type systems — TypeScript, Scala, and Haskell all have some form of static type checking.
Dynamic type systems perform their “type equivalence” evaluation purely at runtime. JavaScript, Python, Ruby, Perl and PHP fall into this category, although there are some great projects like Sorbet(ruby), Mypy(python) and others that bring static type-checking to these languages.
Duck typing
“Duck typing” gets its name from the “duck test”.
“If it looks like a duck, swims like a duck, and quacks like a duck, then it’s probably is a duck”.
In practice, this is very similar to structural typing, but “Duck typing” is usually used to describe dynamic type systems.
“Strong” vs. “Weak” types
These terms, while used frequently, have no agreed-upon technical definition. In the context of TypeScript it’s common for those who say “strong” to really mean “static”.
Nominal vs structural
Nominal type systems are all about names. Let’s take a look at a simple Java example:
javapublic class Car {String make;String model;int make;}public class CarChecker {// takes a `Car` argument, returns a `String`public static String checkCar(Car car) { }}Car myCar = new Car();// TYPE CHECKING// -------------// Is `myCar` type-equivalent to// what `checkCar` wants as an argument?CarChecker.checkCar(myCar);
In the code above, when considering the question of type equivalence on the last line,
all that matters is whether myCar is an instance of the class named Car.
TypeScript’s type system is structural
Structural type systems are all about structure or shape. Let’s look at a TypeScript example:
tsTryclassCar {make : stringmodel : stringyear : numberisElectric : boolean}classTruck {make : stringmodel : stringyear : numbertowingCapacity : number}constvehicle = {make : "Honda",model : "Accord",year : 2017,}functionprintCar (car : {make : stringmodel : stringyear : number}) {console .log (`${car .make } ${car .model } (${car .year })`)}printCar (newCar ()) // FineprintCar (newTruck ()) // FineprintCar (vehicle ) // Fine
The function printCar doesn’t care about which constructor its argument came
from, it only cares about whether it has:
- A
makeproperty that’s of typestring - A
modelproperty that’s of typestring - A
yearproperty that’s of typenumber
If the argument passed to it meets these requirements, printCar is happy.
Revisiting our early discussion involving types as sets of values
The argument passed to printCar has a type that could be described as…
{ all values which contain a... make property which is a string and a model property which is a string and a year property which is a number }
Since every Car instances and all Truck instances meet this criteria, the sets
{all possible Car instances} and {all possible Truck instances} are each subsets of this set.
Going back to the concept
“is type
yequivalent to typex? —> “does the type ofyfit within the type ofx?
In typescript, this is answered as
“is type
yequivalent to typex? —> “is the set represented byya subset of the set representingx?
-
Among these are: generator function
↩yield, accessor/mutator-based property conventions.