How TypeScript handles Type Inference
Predicting code behaviour before execution.
TypeScript's static type system helps us catch errors during development. When we run our code, the JavaScript runtime determines how to behave by identifying the type of each value.
For some values, such as the primitives string and number, we can identify the type at runtime using the typeof operator. However, for other constructs like functions, there is no corresponding runtime mechanism to identify their specific types or signatures.
In the example below, the JavaScript interpreter accesses a property called toLowerCase and calls it. In the next line, it attempts to call message directly.
Ex
const message = "Hello World"
message.toLowerCase();
//calling mesage
message();
Assuming we don't know the initial value of message, we cannot reliably predict the results of running this code. The behaviour of each operation depends entirely on the value stored in message and whether that value is callable.
If we run message.toLowerCase(), we receive the same string in lowercase. However, the next line calls message(), and
the code fails with an exception: TypeError: message is not a function.
Predicting what code will do before it runs isn't always as straightforward as the example above. Consider the following:
function fn(x) {
return x.flip();
}
This function will only work if it is passed an object with a callable flip method. Because JavaScript lacks a static type system, it cannot alert us to type errors before the code is executed. The only type-checking JavaScript provides is dynamic typing —running the code to see what happens.
In this context, a type is a concept describing which values can be safely passed to a function. TypeScript acts as a type-checker to enforce these descriptions.
Static type systems describe the shapes and behaviors of our values before we run our programs. Consequently, running the first sample with TypeScript will trigger an error message before the code ever executes:
const message = "Hello World";
message();
// This expression is not callable.
// Type 'String' has no call signatures.
While JavaScript provides primitives like string and number, it does not verify that you have assigned or used them consistently. TypeScript, on the other hand, does.
In the following example, TypeScript highlights unexpected behavior when calling a property that does not exist on an object.
This is a significant improvement over the JavaScript interpreter, where the result is often silently ambiguous:
const user = {
name: "Tom",
age: 32,
};
user.location;
// > returns undefined
In TypeScript, the same code produces a clear error:
interface User {
name: string;
age: number;
}
const user: User = {
name: "Tom",
age: 32,
};
user.location;
// > Property 'location' does not exist on type '{ name: string; age: number; }'.
