Introduction
The behavior of TypeScript, what it permits and forbids depends on a very fundamental concept called the Type System.
What is a "Type System"?
A “type” is a description of what a JavaScript value shape might be. By “shape” I mean which properties and methods exist on a value, and what the built-in typeof operator would describe it as.
TypeScript is a superset of Javascript and the basic types in TS corresponds to the basic primitives in JavaScript. They are:
null - null
undefined - undefined
boolean - true, false
string - "Prophet", "Car"
number - 1, 2, 4.5, 9.4
bigint - // 0n, 2n, -4n
symbol - Symbol("Franklin");
A type system is a set of rules for how a programming language understands what types the constructs in a program may have.
The type system in typescript operates in 4 basic steps:
It reads your code and understands all the types and values present.
For each value, it sees the initial declaration and indicates what type it may contain.
It then predicts all the ways each of these values can be used later in the code.
Lastly, it complains if the value's usage doesn't match the identified type.
Let me give a quick example of this.
let profession = "Banker";
profession.length();
~~~~~~
// This expression is not callable.
// Type 'Number' has no call signatures
TypeScript read the code and understood that there exists a variable named profession.
Based on the initial value, it determined that it is of type string
It sees that the code is trying to access the .length member of profession and call it like a function.
It complains because the type of .length is a number and a number can't be called like a function
The above error is called a TYPE ERROR.
Error Types
There are two main types of errors in typescript.
Syntax Errors
Type Errors
Syntax Errors
This occurs when typescript detects a wrong syntax in your code. It by default, prevents typescript from properly generating output javascript from your file (Depending on the tooling).
Type Errors
Here, your syntax is probably right, but TypeScript has noticed an improper use of types within your program code. By default, it doesn't prevent your code from being converted to JavaScript, but it does indicate that when it does, something will probably break at runtime.
Assignability
In simple terms, it indicates the type of values that can be assigned to a variable. As you remember, the 2nd step of the type system is that "it sees the initial declaration and indicates what type it may contain". This predicted type would restrict the values that can be assigned to that variable to values of that same type. This is to protect the code from receiving unexpected values that can break it at runtime.
let country = 'Nigeria';
country = 'Canada';
From the code above, typescript can tell that the type of country is a string, and therefore later in the code, any other string can be assigned to it without an error.
let country = 'Nigeria';
country = false;
// Error: Type 'boolean' is not assignable to type 'string'.
The above block of code will cause typescript to complain. The reason is, typescript has identified that country is of type string, but later in the code, we are trying to assign false, a boolean to it. Therefore, it complains by giving the error seen above.
Type annotations
A lot of times when coding, we may want to declare a variable without giving it an initial value yet. By default, if we do this, typescript will implicitly give it a type any. But the any type defeats the purpose of typescript all together.
In fact, at any point in the code, a value of any random type can be assigned to the variable and it will assume that type at that point in the code. Later in the code, a value of a different type can still be assigned to that variable and it still accepts it and assume that type. This makes our code not type-safe.
let flightClass; // Type any
flightClass = "First Class"; // Type string
flightClass.toUpperCase(); // This works fine
flightClass = 1.8; // Type number
flightClass.toPrecision(1); // This works fine
flightClass.toUpperCase(); //
// Error: 'toUpperCase' does not exist on type 'number'.
The same method .toUpperCase()
that worked earlier in the code now causes an error. This is the disadvantage of the any type.
Thankfully, typescript provides a way of indicating the type of a variable without giving it an initial value. This is called type annotations.
let flightClass: string;
flightClass = "First Class"; // This works fine
flightClass.toUpperCase(); // This works fine
flightClass = 1.8;
// Error: Type 'number' is not assignable to type 'string'.
We can see that this prevents us from assigning values of unintended types to our variable, thereby making it safe and less prone to runtime errors.
Type Shapes
Apart from checking the type of values assigned to a variable, typescript also knows the member properties or methods present in an object in our code and it makes sure that you only attempt to access properties that are present in that object.
Note: Strings and numbers also have native properties and methods.
let name = 'Prophet'
name.length // This works fine.
name.toUpperCase() //This also works fine
name.push('o') // Property 'push' does not exist on type 'string'
When an object with properties is declared in TypeScript, the language keeps track of all the properties associated with the object. The properties that exist on the object can be accessed without an error, but trying to access a property that doesn't exist on the object causes typescript to complain.
let student = {
name: "Ben Shapiro",
age: 24,
id: "M12902"
}
student.name // This works fine
student.address
// Property 'address' does not exist on type { name: "Ben Shapiro", age: 24, id: "M12902" }