The compiler will check the types of your code. It has several primitive types and you can define new types yourself. Based on these types, the compiler will warn when a value of a type is used in an invalid manner. That could be using a string for multiplication or using a property of an object that does not exist. The following code would show these errors:
let x = "foo"; x * 2; x.bar();
TypeScript has a special type, called any
, that allows everything; you can assign every value to it and you will never get type errors. The type any
can be used if you do not have an exact type (yet
), for instance, because it is a complex type or if it is from a library that was not written in TypeScript. This means that the following code gives no compile errors:
let x: any = "foo"; x * 2; x.bar();
In the next sections, we will discover these types and learn how the compiler finds these types.
TypeScript has several primitive types, which are listed in the following table:
Name |
Values |
Example |
boolean |
|
let x: boolean = true;
|
string |
Any string literal |
let x: string = "foo";
|
number |
Any number, including |
let x: number = 42; let y: number = NaN;
|
Literal types |
Literal types can only contain one value |
let x: "foo" = "foo";
|
void |
Only used for a function that does not return a value |
function a(): void { }
|
never |
No values | |
any |
All values |
let x: any = "foo"; let y: any = true;
|
You can define your own types in various ways:
Kind |
Meaning |
Example |
Object type |
Represents an object, with the specified properties. Properties marked with Object types can be defined inline, with a class or with an interface declaration. |
let x: { a: boolean, b: string, c?: number, [i: number]: string }; x = { a: true, b: "foo" }; x[0] = "foo";
|
Union type |
A value is assignable to a union type if it is assignable to one of the specified types. In the example, it should be a string or a number. |
let x: string | number; x = "foo"; x = 42;
|
Intersection type |
A value is assignable to an intersection type if it is assignable to all specified types. |
let x: { a: string } & { b: number } = { a: "foo", b: 42 };
|
Enum type |
A special number type, with several values declared. The declared members get a value automatically, but you can also specify a value. |
enum E { X, Y = 100 } let a: E = E.X;
|
Function type |
Represents a function with the specified arguments and return type. Optional and rest arguments can also be specified. |
let f: (x: string, y?: boolean) => number; let g: (...xs: number[]) => number;
|
Tuple type |
Multiple values are placed in one, as an array. |
let x: [string, number]; X = ["foo", 42];
|
By default, undefined
and null
can be assigned to every type. Thus, the compiler cannot give you a warning when a value can possibly be undefined or null. TypeScript 2.0 has introduced a new mode, called strictNullChecks
, which adds two new types: undefined
and null
. With that mode, you do get warnings in such cases. We will discover that mode in Chapter 6, Advanced Programming in TypeScript.
TypeScript can infer some types. This means that the TypeScript compiler knows the type, without a type annotation. If a type cannot be inferred, it will default to any
. In such a case, or in case the inferred type is not correct, you have to specify the types yourself. The common declarations that you can annotate are given in the following table:
Location |
Can it be inferred? |
Examples |
Variable declaration |
Yes, based on initializer |
let a: number; let b = 1;
|
Function argument |
Yes, based on default value (second example) or when passing the function to a typed variable or function (third example) |
function a(x: number) {} function b(x = 1) {} [1, 2].map( x => x * 2 );
|
Function return type |
Yes, based on return statements in body |
function a(): number { } (): number => { } function c() { return 1; }
|
Class member |
Yes, based on default value |
class A { x: number; y = 0; }
|
Interface member |
No |
interface A { x: number; }
|
You can set the compiler option noImplicitAny
to get compiler errors when a type could not be inferred and falls back to any
. It is advised to use that option always, unless you are migrating a JavaScript codebase to TypeScript. You can read about such migration in Chapter 10,Migrate JavaScript to TypeScript.