The official TypeScript documentation has been updated for a long time, but the Chinese documentation I could find was still in older versions. Therefore, some new and revised chapters are translated and sorted out.

This translation is from the “Everyday Types” chapter in the TypeScript Handbook.

This paper does not strictly follow the translation of the original text, and some of the content has also been explained and supplemented.

Everyday Types

In this chapter we’ll look at some of the most common types in JavaScript and how they are described. Note that this chapter is not exhaustive; subsequent chapters will explain more about naming and using types.

Types can appear in many places, not just in Type annotations. We need to learn not only about the types themselves, but also where to use them to produce new structures.

Let’s start by reviewing the most basic and common types, which are the basis for building more complex types.

Original type:string.numberboolean(The primitives)

JavaScript has three very common primitive types: string, number, and Boolean, each of which has a TypeScript counterpart. Their names are the same as what you get when you use the Typeof operator in JavaScript.

  • stringFor strings, like “Hello, world”
  • numberNumbers, for example42Not in JavaScriptintorfloatAll numbers, all typesnumber
  • booleanRepresents a Boolean value, and there are only two values:truefalse

The type names String, Number, and Boolean (capitalized) are also legal, but they are some very rare special built-in types. So the type is always string, number, or Boolean.

Array

To declare an array type like [1, 2, 3], you need the syntax number[]. This syntax can be applied to any type (for example, string[] represents an array of strings). You might also see Array

, same thing. We will introduce T
syntax in the generics section.

Note that [number] and number[] have different meanings, refer to the tuple section

any

TypeScript has a special type, any, that you can set to when you don’t want a value to cause type checking errors.

When a value is of type any, you can get any of its attributes (which will also be converted to any), call it like a function, assign it to a value of any type, or assign it to a value of any type, or do any other syntactically correct operations:

let obj: any = { x: 0 };
// None of the following lines of code will throw compiler errors.
// Using `any` disables all further type checking, and it is assumed 
// you know the environment better than TypeScript.
obj.foo();
obj();
obj.bar = 100;
obj = "hello";
const n: number = obj;
Copy the code

The any type is useful when you don’t want to write long type code and just want TypeScript to know that a particular piece of code is ok.

noImplicitAny

If you don’t specify a type, and TypeScript can’t infer its type from the context, the compiler defaults to any.

If you always want to avoid this — TypeScript doesn’t type check any, after all — you can turn on the compiler noImplicitAny, which will report an error when it implicitly inferences to any.

Type Annotations on Variables

When you declare a variable using const, var, or let, you can optionally add a type annotation that explicitly specifies the type of the variable:

let myName: string = "Alice";
Copy the code

TypeScript does not use “type declaration on the left”, such as int x = 0; Type annotations often follow the content of the type to be declared.

But most of the time, you don’t have to. TypeScript automatically inferences types. For example, the type of a variable can be inferred based on the initial value:

// No type annotation needed -- 'myName' inferred as type 'string'
let myName = "Alice";
Copy the code

Most of the time, you don’t need to learn the rules of inference. If you’re just starting out, try to use type annotations as little as possible. You might be surprised how little TypeScript needs to fully understand what’s going on.

Functions

Functions are the primary way JavaScript passes data. TypeScript allows you to specify the types of input and output values for functions.

Parameter Type Annotations

When you declare a function, you can add a type annotation after each argument to declare what type of arguments the function can accept. The parameter type annotation follows the parameter name:

// Parameter type annotation
function greet(name: string) {
  console.log("Hello, " + name.toUpperCase() + "!!!!!");
}
Copy the code

When arguments have type annotations, TypeScript checks function arguments:

// Would be a runtime error if executed!
greet(42);
// Argument of type 'number' is not assignable to parameter of type 'string'.
Copy the code

Even if you don’t type the parameters, TypeScript still checks that the correct number of parameters are passed in

Return Type Annotations

You can also add type annotations for return values. The type annotation for the return value follows the parameter list:

function getFavoriteNumber() :number {
  return 26;
}
Copy the code

Like variable type annotations, you don’t always need to add return type annotations; TypeScript infer the return type of a function based on its return statement. As in this example, type annotations are written and not written identically, but some code bases explicitly specify the type of the return value, perhaps because of documentation, or to prevent accidental changes, or simply as a matter of personal preference.

Anonymous Functions

Anonymous functions differ from function declarations in that when TypeScript knows how an anonymous function will be called, its parameters are automatically typed.

Here’s an example:

// No type annotations here, but TypeScript can spot the bug
const names = ["Alice"."Bob"."Eve"];
 
// Contextual typing for function
names.forEach(function (s) {
  console.log(s.toUppercase());
  // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
 
// Contextual typing also applies to arrow functions
names.forEach((s) = > {
  console.log(s.toUppercase());
  // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
Copy the code

Although the parameter s does not have a type annotation, TypeScript eventually extrapolates the type of s based on the type of the forEach function and the type of the array passed in.

This process is known as contextual typing because it is from the context in which a function appears that the type it should have.

Like the rule of inference, you don’t need to learn how it happens, just know that it exists and helps you avoid unnecessary annotations. We’ll see more examples of how the context in which a value appears affects its type later.

Object Types

After primitive types, the most common types are object types. To define an object type, we simply list its properties and corresponding types.

Here’s an example:

// The parameter's type annotation is an object type
function printCoord(pt: { x: number; y: number }) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3.y: 7 });
Copy the code

Here, we add a type to the parameter that has two attributes, x and y, both of type number. You can use, or; Separate attributes with or without the delimiter of the last attribute.

The type for each attribute is optional; if you do not specify it, the default type is any.

Optional Properties

Object types can specify some or all of the properties as optional, you just need to add one after the property name, right? :

function printName(obj: { first: string; last? :string }) {
  // ...
}
// Both OK
printName({ first: "Bob" });
printName({ first: "Alice".last: "Alisson" });
Copy the code

In JavaScript, if you get a property that doesn’t exist, you get a undefined instead of a runtime error. Therefore, when you get an optional property, you need to check whether it is undefined before using it.

function printName(obj: { first: string; last? :string }) {
  // Error - might crash if 'obj.last' wasn't provided!
  console.log(obj.last.toUpperCase());
  // Object is possibly 'undefined'.
  if(obj.last ! = =undefined) {
    // OK
    console.log(obj.last.toUpperCase());
  }
 
  // A safe alternative using modern JavaScript syntax:
  console.log(obj.last? .toUpperCase()); }Copy the code

Union Types

The TypeScript type system allows you to build new types based on existing types using a series of operators. Now that we know how to write some basic types, it’s time to put them together.

Define a Union Type (Defining a Union Type)

The first way to combine types is to use a union type, where a union type is a type made up of two or more types and the representation value can be any one of those types. Each of these types is a member of the union type.

Let’s write a function that handles strings or numbers:

function printId(id: number | string) {
  console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
printId({ myID: 22342 });
// Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.
// Type '{ myID: number; }' is not assignable to type 'number'.
Copy the code

Working with Union Types

Providing a value that matches the union type is easy; you only need to provide a value that matches any of the union member types. So once you have a value of union type, how do you use it?

TypeScript requires you to do things that are valid for each member of the association. For example, if you have a joint type string | number, you can’t use only exist on the string method:

function printId(id: number | string) {
  console.log(id.toUpperCase());
    // Property 'toUpperCase' does not exist on type 'string | number'.
    // Property 'toUpperCase' does not exist on type 'number'.
}
Copy the code

The solution is to narrow the union type with code, just as you would in JavaScript without type annotations. Type narrowing occurs when TypeScript can infer a more specific type from the structure of the code.

For example, TypeScript knows that using Typeof on a string value returns the string” string” :

function printId(id: number | string) {
  if (typeof id === "string") {
    // In this branch, id is of type 'string'
    console.log(id.toUpperCase());
  } else {
    // Here, id is of type 'number'
    console.log(id); }}Copy the code

For another example, use functions such as array. isArray:

function welcomePeople(x: string[] | string) {
  if (Array.isArray(x)) {
    // Here: 'x' is 'string[]'
    console.log("Hello, " + x.join(" and "));
  } else {
    // Here: 'x' is 'string'
    console.log("Welcome lone traveler "+ x); }}Copy the code

Note that in the else branch, we don’t need to do anything special, if x is not string[], then it must be string.

Sometimes, if each member of a union type has an attribute, for example, the slice method for both numbers and strings, you can use this attribute directly without type narrowing:

// Return type is inferred as number[] | string
function getFirstThree(x: number[] | string) {
  return x.slice(0.3);
}
Copy the code

Joint type you may be very strange, why can only use the type attribute of the intersection, let us take, for example, there are two rooms, one room is eight feet tall hat, another room is a hat can speak Spanish, merge the two rooms, the only thing we know is: every man wearing a hat.

The TypeScript series

  1. The basics of TypeScript
  2. TypeScript type narrowing
  3. The function of TypeScript
  4. TypeScript object type
  5. The generic of TypeScript
  6. TypeScript’s Keyof operator
  7. TypeScript’s Typeof operator
  8. TypeScript index access types
  9. TypeScript conditional types

Wechat: “MQyqingfeng”, add me Into Hu Yu’s only readership group.

If there is any mistake or not precise place, please be sure to give correction, thank you very much. If you like or are inspired by it, welcome star and encourage the author.