All that variable type stuff

1. Basic notes

Type annotations use the: TypeAnnotation syntax. Anything available in the type declaration space can be used as a type annotation.

const num: number = 123;
function identity(num:number):number{
    return num;
}

Copy the code

Error writing after adding comments:

const num: number = 123;
function identity(num: number): number {
  const num1 = '123'// Return a number errorreturn num1;
}
const num1 = '123'// identity(num1)Copy the code

2. Primitive types

Javascript primitive types also work with TypeScript’s type system. Therefore, string, number, and Boolean can also be used as type annotations:

 let num: number;
 let str: string;
 let bool: boolean;

 num = 123;
 num = 123.45;
 num = '123'; //Type '" 123" is not assignable to type 'number'

 str = '123';
 str = 123; //Type '123' is not assignable to type 'string'

 bool = true;
 bool = false;
 bool = 'false'; //Type'"false"' is not assignable to type 'boolean'.
Copy the code

An array of 3.

TypeScript provides a special type syntax for arrays, so you can easily annotate arrays. It uses the suffix [], and you can then add any valid type annotations (e.g., Boolean []) as needed. It allows you to safely use any operation on an array, and it can place some behavior like assigning an error type to a member.

You can define arrays in two ways:

First, you can add [] to the element type to represent an array of elements of that type:

letList: the number [] = [1, 2, 3]Copy the code

The second way is to use Array generics, Array< element type >:

letList: Array < number > = [1, 2, 3]Copy the code

A specific chestnut:

const boolArray: boolean[];

boolArray = [true.false];
console.log(boolArray[0]); // true
console.log(boolArray.length); // 2

boolArray[1] = true;
boolArray = [false.false];

boolArray[0] = 'false'; // Error
boolArray = 'false'; // Error
boolArray = [true.'false']; // Error
Copy the code

4. A tuple

The tuple type allows you to represent an array with a known number and type of elements that need not be of the same type. For example, you can define a pair of tuples of type string and number.

// Declare a tuple typeletx: [string, number]; // initialize assignment x = ['hello'10]; // OK // initialization error x = [10,'hello']; // Error
Copy the code

When accessing an element with a known index, we get the correct type:

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
Copy the code

??????????????? When accessing an out-of-bounds element, the union type is used instead:

x[3] = 'world'; / / OK, strings can be assigned to (string | number) typeCopy the code

Index ‘2’ is out of bounds in tuple of length 2.

5. The enumeration

Enum types complement the JavaScript standard data types. Enumeration types are used to give friendly names to a set of values.

enum Color {Red, Green, Blue}
let c: Color = Color.Green;
Copy the code

By default, elements are numbered from 0. You can also manually specify member numbers. For example, let’s change the chestnuts above to numbered from 1:

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
Copy the code

Or use all manual assignments:

enum Color {Red = 1,Geeen = 2, Blue = 4}
let c: Color = Color.Green
Copy the code

Compiled JS

var Color;
(function (Color) {
    Color[Color["Red"]] = = 1"Red";
    Color[Color["Green"] = = 2]"Green";
    Color[Color["Blue"] = = 4]"Blue";
})(Color || (Color = {}));
var c = Color.Green;
Copy the code

All expressions have a return value, and its return value is the assignment to the right of the equals sign.

One traversal provided by an enumeration type is the name you can get from the enumeration value. For example, if we know that the value is 2, but are not sure which name in Color it maps to, we can look for the corresponding name:

enum Color {Red = 1, Green, Blue}
letcolorName: string = Color[2]; console.log(colorName); / / show'Green'Because it has a value of 2 in the code aboveCopy the code

This one tried not to give a number, but it couldn’t get through

enum Color {Red = 'r', Green = 'g', Blue = 'b'}
let c: Color = Color.Green;
console.log(c) // g
let colorName:string = Color[2]
console.log(colorName) // undefined
Copy the code

Let’s take a look at the compiled javascript code:

var Color;
(function (Color) {
   Color["Red"] = "r";
   Color["Green"] = "g";
   Color["Blue"] = "b";
})(Color || (Color = {}));
var c = Color.Green;
console.log(c);
var colorName = Color[2];
console.log(colorName);
Copy the code

6. Special types

6.1 any

The any type has a special place in the TypeScript type system. It gives you a back door to the type system, and TypeScript turns type checking off. Any is compatible with all types (including itself) in the type system. Therefore, all types can be assigned to it. It can also be assigned to any other type.

letpower: any; // Assign any type of power ='123'
power = 123
let num:number;
num = power;
power = num;
Copy the code

6.2 null, and undefined

In the type system, JavaScript null and undefined literals can be assigned to variables of any type, just like any variables labeled with type any, as shown in the following example:

let num: numer;
letstr: string; // These types can be given num = null; str = undefined;Copy the code

6.3 void

Use :void to indicate that a function does not have a return value

function log(message:string):void{
    console.log(message)
}
Copy the code

Declaring a void variable doesn’t do much good, because you can only assign undefined and null to it:

let unusable:void = undefined
Copy the code

7. The generic

In software engineering, it is important not only to create consistent, well-defined apis, but also to consider reusability.

The ability to support not only current data types but also future data types gives you a lot of flexibility when building large systems.

Take a simple evolution example:

When generics are not used, your code might look like this:

function identity(arg:number):number{
    return arg;
}
Copy the code

This function can accept type number and return type number. For reusability purposes, or we can use type any to define functions like this:

function identity(arg:any):any{
    return arg;
}
Copy the code

Using any causes the function to accept any type of ARG argument, and any type of value may be returned.

We need a way to make the type of the return value the same as the type of the parameter passed in. Next we can put the code like this:

function identity<T>(arg:T):T{
    return arg
}
Copy the code

We add the type variable T to identity. If the type is passed in (for example :number), we know that the returned type is number as well. Now we know that the parameter type is the same as the return value type. This helps us keep track of the type used in the function.

We call this version of the identity function generic. It can use the uncertainty of multiple types, unlike any, and keep the accuracy of the first example, where the parameter type is the same as the return value type.

7.1 Use of generics

We have defined the fire of generic functions, which can be used in two ways.

The first is to pass in all the arguments, including the type arguments:

let output = identity<string>('myString'); //type of output will be 'string'
Copy the code

Here we explicitly specify T as a string, pass it to the function as an argument, and make sure output is of type string.

Note ⚠️ : use <> instead of ()

The second method uses type inference – that is, the compiler automatically helps us determine the type of T based on the parameters passed in.

let output = identity("myString"); //type of output will be 'string'
Copy the code

There is no use of (<>) to pass in the type explicitly; The compiler can look at the value of myString and set T to its type.

7.2 Using generic variables

When creating a generic function like Identity using a generic type, the compiler requires that you use the generic type correctly in the function body. In other words, you must treat these parameters as any or all types.

Take a look at the generic example we wrote earlier:

function identity<T>(arg:T):T{
    return arg
}
Copy the code

When we try to print arg. Length inside a function, the compiler will error that the attribute “length” does not exist on type “T”.

function idenfity<T>(arg:T):T{
 console.log(arg.length) // Error: T doesn't have .length return arg }Copy the code

Note ⚠ ️ :

The type variable T represents any type, so someone using this function might pass in a number that doesn’t have a.length attribute.

And then let’s pretend that we’re operating on an array of type T instead of just T, because we’re operating on an array, the dot length property should be there.

The code looks like this:

function loggingIdentity<T>(arg: T[]):T[]{
console.log(arg.length) // Array has a .length,so no more error
return arg
}
Copy the code

The generic function loggingIdentity takes the type parameter T and the parameter arg, which is an array of elements of type T, and returns an array of elements of type T. If we pass in an array of numbers, we return an array of numbers, because T is of type number. This allows us to use the generic variable T as part of the type, rather than the whole type, adding flexibility.

We could also implement the above example like this:

function loggingIdentity<T>(arg:Array<T>):Array<T>{
console.log(arg.length)
return arg
}
Copy the code

In computer science, many algorithms and data structures do not depend on the actual type of the object. However, you will still want to enforce constraints in each variable.

For example, in a function that takes a list and returns the reverse order of the list, the constraints are the arguments passed to the function and the return value of the function:

function reverse<T>(items:T[]):T[]{
  const toreturn = []
  for(leti = items.length - 1; i>=0; i--){ toreturn.push(items[i]) }returnToreturn} const sample = [1,2,3]let reversed = reverse(sample)

console.log(reversed)

reversed[0] = '1'; // Error
reversed = ['1'.'2']; // Error

reversed[0] = 1; // ok
reversed = [1, 2]; // ok
Copy the code

In this case, the function reverse takes an array of items:T[] of type T and returns an array of values of type T (note :T[]). The function reverse returns a value of the same type as the argument it accepts. When you pass var sample = [1, 2, 3], TypeScript can infer that reverse is of type number[], giving you type safety. Similarly, when you pass an array of type string[], TypeScript can infer that it is string[], as in:

const strArr = ['1'.'2']
let reversedStrs = reverse(strArr)
reversedStrs = [1, 2]; // Error
Copy the code

If your array is const sample = [1,false,3] = const sample: (number | Boolean) [], so the following code can also can, if is there are two or even three types in the array, it is able to infer (number | Boolean) this or type. This is the union type described below.

function reverse<T>(items:T[]):T[]{
  const toreturn = []
  for(leti = items.length - 1; i>=0; i--){ toreturn.push(items[i]) }return toreturn
}

const sample = [1,false, 3]let reversed = reverse(sample)

console.log(reversed)

reversed[0] = true; // OK
reversed = [3, true]; // OK

reversed[0] = 1; // ok
reversed = [1, 2]; // ok
Copy the code

8. Union types

The union type indicates that the value can be one of many types.

8.1 Simple Examples

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'
myFavoriteNumber = 7
Copy the code
let myFavoriteNumber: string | number;
myFavoriteNumber = true;

// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.
//   Type 'boolean' is not assignable to type 'number'

Copy the code

Joint type using | separated for each type.

Here let myFavoriteNumber: string | number; The myFavoriteNumber type is allowed to be string or number, but not other types.

8.2 Accessing properties or methods of the union type

When TypeScript doesn’t know what type a variable of a union type is, we can only access properties or methods that are common to all types of the union type.

function getLength(something:string|number):number{
returnSomething. Length} / / the Error type "string | number" there is no attribute "length". The attribute "length" does not exist on type "number".Copy the code

In this example, length is not a common attribute of string and number, so an error is reported.

There is no problem accessing the common attributes of string and number:

function getLength(something:string|number):string{
    return something.toString()
}
Copy the code

When a variable of the union type is assigned, it can infer a type according to the rules of type inference:

let myFavoriteNumber:string|number;
myFavoriteNumber = 'seven'; console.log(myFavoriteNumber.length) myFavoriteNumber = 7 console.log(myFavoriteNumber.length) // Rrror The attribute "length" does not exist on type "number".Copy the code

In the example above, the myFavoriteNumber in line 2 is inferred to string, and accessing its length attribute is not an error. The fourth line, myFavoriteNumber, is inferred to number and an error is reported when accessing its length property.

A common use case is a function that can accept a single object or an array of objects:

function formatCommandline(command: string[] | string) {
  let line = ' ';
  if (typeof command= = ='string') {
    line = command.trim();
  } else {
    line = command.join(' ').trim();
  }

  // Do stuff with line: string
}
Copy the code