For programs to be useful, we need to be able to work with some of the simplest units of data: numbers, strings, structures, boolean values, and the like. In TypeScript, we support the same types as you would expect in JavaScript, with an extra enumeration type thrown in to help things along.

Boolean

The most basic datatype is the simple true/false value, which JavaScript and TypeScript call a boolean value.

  1. let isDone: boolean = false;Try

Number

As in JavaScript, all numbers in TypeScript are either floating point values or BigIntegers. These floating point numbers get the type number, while BigIntegers get the type bigint. In addition to hexadecimal and decimal literals, TypeScript also supports binary and octal literals introduced in ECMAScript 2015.

  1. let decimal: number = 6;
  2. let hex: number = 0xf00d;
  3. let binary: number = 0b1010;
  4. let octal: number = 0o744;
  5. let big: bigint = 100n;Try

String

Another fundamental part of creating programs in JavaScript for webpages and servers alike is working with textual data. As in other languages, we use the type string to refer to these textual datatypes. Just like JavaScript, TypeScript also uses double quotes (") or single quotes (') to surround string data.

  1. let color: string = "blue";
  2. color = "red";Try

You can also use template strings, which can span multiple lines and have embedded expressions. These strings are surrounded by the backtick/backquote (` ) character, and embedded expressions are of the form ${ expr }.

  1. let fullName: string = `Bob Bobbington`;
  2. let age: number = 37;
  3. let sentence: string = `Hello, my name is ${fullName}.
  4. I'll be ${age + 1} years old next month.`;Try

This is equivalent to declaring sentence like so:

  1. let sentence: string =
  2. "Hello, my name is " +
  3. fullName +
  4. ".\n\n" +
  5. "I'll be " +
  6. (age + 1) +
  7. " years old next month.";Try

Array

TypeScript, like JavaScript, allows you to work with arrays of values. Array types can be written in one of two ways. In the first, you use the type of the elements followed by [] to denote an array of that element type:

  1. let list: number[] = [1, 2, 3];Try

The second way uses a generic array type, Array<elemType>:

  1. let list: Array<number> = [1, 2, 3];Try

Tuple

Tuple types allow you to express an array with a fixed number of elements whose types are known, but need not be the same. For example, you may want to represent a value as a pair of a string and a number:

  1. // Declare a tuple type
  2. let x: [string, number];
  3. // Initialize it
  4. x = ["hello", 10]; // OK
  5. // Initialize it incorrectly
  6. x = [10, "hello"]; // Error
  7. Type 'number' is not assignable to type 'string'.
    Type 'string' is not assignable to type 'number'.2322
    2322Type 'number' is not assignable to type 'string'.
    Type 'string' is not assignable to type 'number'.
    Try

When accessing an element with a known index, the correct type is retrieved:

  1. // OK
  2. console.log(x[0].substring(1));
  3. console.log(x[1].substring(1));
  4. Property 'substring' does not exist on type 'number'.2339Property 'substring' does not exist on type 'number'.Try

Accessing an element outside the set of known indices fails with an error:

  1. x[3] = "world";
  2. Tuple type '[string, number]' of length '2' has no element at index '3'.2493Tuple type '[string, number]' of length '2' has no element at index '3'.
  3. console.log(x[5].toString());
  4. Object is possibly 'undefined'.
    Tuple type '[string, number]' of length '2' has no element at index '5'.2532
    2493Object is possibly 'undefined'.
    Tuple type '[string, number]' of length '2' has no element at index '5'.
    Try

Enum

A helpful addition to the standard set of datatypes from JavaScript is the enum. As in languages like C#, an enum is a way of giving more friendly names to sets of numeric values.

  1. enum Color {
  2. Red,
  3. Green,
  4. Blue,
  5. }
  6. let c: Color = Color.Green;Try

By default, enums begin numbering their members starting at 0. You can change this by manually setting the value of one of its members. For example, we can start the previous example at 1 instead of 0:

  1. enum Color {
  2. Red = 1,
  3. Green,
  4. Blue,
  5. }
  6. let c: Color = Color.Green;Try

Or, even manually set all the values in the enum:

  1. enum Color {
  2. Red = 1,
  3. Green = 2,
  4. Blue = 4,
  5. }
  6. let c: Color = Color.Green;Try

A handy feature of enums is that you can also go from a numeric value to the name of that value in the enum. For example, if we had the value 2 but weren’t sure what that mapped to in the Color enum above, we could look up the corresponding name:

  1. enum Color {
  2. Red = 1,
  3. Green,
  4. Blue,
  5. }
  6. let colorName: string = Color[2];
  7. // Displays 'Green'
  8. console.log(colorName);Try

Unknown

We may need to describe the type of variables that we do not know when we are writing an application. These values may come from dynamic content – e.g. from the user – or we may want to intentionally accept all values in our API. In these cases, we want to provide a type that tells the compiler and future readers that this variable could be anything, so we give it the unknown type.

  1. let notSure: unknown = 4;
  2. notSure = "maybe a string instead";
  3. // OK, definitely a boolean
  4. notSure = false;Try

If you have a variable with an unknown type, you can narrow it to something more specific by doing typeof checks, comparison checks, or more advanced type guards that will be discussed in a later chapter:

  1. declare const maybe: unknown;
  2. // 'maybe' could be a string, object, boolean, undefined, or other types
  3. const aNumber: number = maybe;
  4. Type 'unknown' is not assignable to type 'number'.2322Type 'unknown' is not assignable to type 'number'.
  5. if (maybe === true) {
  6. // TypeScript knows that maybe is a boolean now
  7. const aBoolean: boolean = maybe;
  8. // So, it cannot be a string
  9. const aString: string = maybe;
  10. Type 'boolean' is not assignable to type 'string'.2322Type 'boolean' is not assignable to type 'string'.}
  11. if (typeof maybe === "string") {
  12. // TypeScript knows that maybe is a string
  13. const aString: string = maybe;
  14. // So, it cannot be a boolean
  15. const aBoolean: boolean = maybe;
  16. Type 'string' is not assignable to type 'boolean'.2322Type 'string' is not assignable to type 'boolean'.}Try

Any

In some situations, not all type information is available or it’s declaration would take an inappropriate amount of effort. These may occur for values from code that has been written without TypeScript or a 3rd party library. In these cases, we might want to opt-out of type checking. To do so, we label these values with the any type:

  1. declare function getValue(key: string): any;
  2. // OK, return value of 'getValue' is not checked
  3. const str: string = getValue("myString");Try

The any type is a powerful way to work with existing JavaScript, allowing you to gradually opt-in and opt-out of type checking during compilation.

Unlike unknown, variables of type any allow you to access arbitrary properties, even ones that don’t exist. These properties include functions and TypeScript will not check their existence or type:

  1. let looselyTyped: any = 4;
  2. // OK, ifItExists might exist at runtime
  3. looselyTyped.ifItExists();
  4. // OK, toFixed exists (but the compiler doesn't check)
  5. looselyTyped.toFixed();
  6. let strictlyTyped: unknown = 4;
  7. strictlyTyped.toFixed();
  8. Object is of type 'unknown'.2571Object is of type 'unknown'.Try

The any will continue to propagate through your objects:

  1. let looselyTyped: any = {};
  2. let d = looselyTyped.a.b.c.d;
  3. // ^ = let d: anyTry

After all, remember that all the convenience of any comes at the cost of losing type safety. Type safety is one of the main motivations for using TypeScript and you should try to avoid using any when not necessary.

Void

void is a little like the opposite of any: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value:

  1. function warnUser(): void {
  2. console.log("This is my warning message");
  3. }Try

Declaring variables of type void is not useful because you can only assign null (only if --strictNullChecks is not specified, see next section) or undefined to them:

  1. let unusable: void = undefined;
  2. // OK if `--strictNullChecks` is not given
  3. unusable = null;Try

Null and Undefined

In TypeScript, both undefined and null actually have their own types named undefined and null respectively. Much like void, they’re not extremely useful on their own:

  1. // Not much else we can assign to these variables!
  2. let u: undefined = undefined;
  3. let n: null = null;Try

By default null and undefined are subtypes of all other types. That means you can assign null and undefined to something like number.

However, when using the --strictNullChecks flag, null and undefined are only assignable to unknown, any and their respective types (the one exception being that undefined is also assignable to void). This helps avoid many common errors. In cases where you want to pass in either a string or null or undefined, you can use the union type string | null | undefined.

Union types are an advanced topic that we’ll cover in a later chapter.

As a note: we encourage the use of --strictNullChecks when possible, but for the purposes of this handbook, we will assume it is turned off.

Never

The never type represents the type of values that never occur. For instance, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns. Variables also acquire the type never when narrowed by any type guards that can never be true.

The never type is a subtype of, and assignable to, every type; however, no type is a subtype of, or assignable to, never (except never itself). Even any isn’t assignable to never.

Some examples of functions returning never:

  1. // Function returning never must not have a reachable end point
  2. function error(message: string): never {
  3. throw new Error(message);
  4. }
  5. // Inferred return type is never
  6. function fail() {
  7. return error("Something failed");
  8. }
  9. // Function returning never must not have a reachable end point
  10. function infiniteLoop(): never {
  11. while (true) {}
  12. }Try

Object

object is a type that represents the non-primitive type, i.e. anything that is not number, string, boolean, symbol, null, or undefined.

With object type, APIs like Object.create can be better represented. For example:

  1. declare function create(o: object | null): void;
  2. // OK
  3. create({ prop: 0 });
  4. create(null);
  5. create(42);
  6. Argument of type '42' is not assignable to parameter of type 'object | null'.2345Argument of type '42' is not assignable to parameter of type 'object | null'.create("string");
  7. Argument of type '"string"' is not assignable to parameter of type 'object | null'.2345Argument of type '"string"' is not assignable to parameter of type 'object | null'.create(false);
  8. Argument of type 'false' is not assignable to parameter of type 'object | null'.2345Argument of type 'false' is not assignable to parameter of type 'object | null'.create(undefined);
  9. Argument of type 'undefined' is not assignable to parameter of type 'object | null'.2345Argument of type 'undefined' is not assignable to parameter of type 'object | null'.Try

Generally, you won’t need to use this.

Type assertions

Sometimes you’ll end up in a situation where you’ll know more about a value than TypeScript does. Usually this will happen when you know the type of some entity could be more specific than its current type.

Type assertions are a way to tell the compiler “trust me, I know what I’m doing.” A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler. TypeScript assumes that you, the programmer, have performed any special checks that you need.

Type assertions have two forms.

One is the as-syntax:

  1. let someValue: any = "this is a string";
  2. let strLength: number = (someValue as string).length;Try

The other version is the “angle-bracket” syntax:

  1. let someValue: any = "this is a string";
  2. let strLength: number = (<string>someValue).length;Try

The two samples are equivalent. Using one over the other is mostly a choice of preference; however, when using TypeScript with JSX, only as-style assertions are allowed.

A note about let

You may have noticed that so far, we’ve been using the let keyword instead of JavaScript’s var keyword which you might be more familiar with. The let keyword is actually a newer JavaScript construct that TypeScript makes available. You can read in the Handbook Reference on Variable Declarations more about how let and const fix a lot of the problems with var.

About Number, String, Boolean, Symbol and Object

It can be tempting to think that the types Number, String, Boolean, Symbol, or Object are the same as the lowercase versions recommended above. These types do not refer to the language primitives however, and almost never should be used as a type.

  1. function reverse(s: String): String {
  2. return s.split("").reverse().join("");
  3. }
  4. reverse("hello world");Try

Instead, use the types number, string, boolean, object and symbol.

  1. function reverse(s: string): string {
  2. return s.split("").reverse().join("");
  3. }
  4. reverse("hello world");Try