Type Inference
# Type Inference
This section covers type inference in TypeScript -- that is, where and how types are inferred.
# Basics
In TypeScript, where no explicit type is given, type inference is used to provide type information. For example:
let x = 3
The type of variable x is inferred to be number. This kind of inference occurs when initializing variables and members, setting default parameter values, and determining function return types.
In most cases, type inference is straightforward. In the following sections, we'll explore the nuances of how types are inferred.
# Best Common Type
Sometimes we need to infer a type from several expressions, and the types of those expressions are used to determine the best common type. For example:
let x = [0, 1, null]
To infer the type of x, we must consider the types of all elements. Here there are two candidates: number and null. The best common type algorithm considers all candidate types and selects a type that is compatible with all of them.
Since the resulting common type is chosen from the candidate types, sometimes candidates share a common structure but none of them is a supertype of all others. For example:
class Animal {
numLegs: number
}
class Bee extends Animal {
}
class Lion extends Animal {
}
let zoo = [new Bee(), new Lion()]
2
3
4
5
6
7
8
9
10
11
Here, we'd like zoo to be inferred as Animal[], but since no element in the array is of type Animal, we can't infer that result. To fix this, we can explicitly declare the type we expect:
let zoo: Animal[] = [new Bee(), new Lion()]
If no best common type is found, the resulting inference is the union array type, (Bee | Lion)[].
# Contextual Typing
Sometimes TypeScript infers types differently through what we call "contextual typing." Contextual typing occurs when the type of an expression is implied by its location. For example:
window.onmousedown = function(mouseEvent) {
console.log(mouseEvent.clickTime) // Error
}
2
3
This example produces a type error. The TypeScript type checker uses the type of the window.onmousedown function to infer the type of the function expression on the right. Thus, it can infer the type of the mouseEvent parameter. Since mouseEvent accessed a property that doesn't exist, an error was reported.
If the contextual type expression contains explicit type information, the contextual type is ignored. Rewriting the above example:
window.onmousedown = function(mouseEvent:any) {
console.log(mouseEvent.clickTime) // OK
}
2
3
This function expression has an explicit parameter type annotation, so the contextual type is ignored. In this case there's no error, because contextual typing isn't applied.
Contextual typing is used in many situations. Common cases include function arguments, the right side of assignments, type assertions, object members, array literals, and return statements. Contextual typing also serves as a candidate for the best common type. For example:
function createZoo(): Animal[] {
return [new Bee(), new Lion()]
}
let zoo = createZoo()
2
3
4
5
In this example, the best common type has 3 candidates: Animal, Bee, and Lion. Animal is selected as the best common type.