Typescript

环境配置

  1. 全局安装 typescript
npm i typescript -g
  1. 示例代码 demo.ts
function hello(): void {
  let str: string = "hello world";
  console.log(str);
}
hello();

因为是 typescript 代码,node 无法直接执行,需要先编译为 javascript 代码
使用命令将 demo.ts 编译为 demo.js 文件

tsc demo.ts

接着运行 demo.js

node demo.js

还可以通过添加 -w 参数,即 tsc -w ,让 TS 编译器处于监控状态,一旦该工程有新的 TS 文件改变,会自动进行编译,不需要再每次手动执行编译。

还可以全局安装 ts-node 库

npm i ts-node -g

使用 ts-node 命令直接运行 ts 代码

ts-node demo.ts

基本数据类型

数据类型 关键字 描述
任意类型 any 声明为 any 的变量可以赋予任意类型的值。
数字类型 number 双精度 64 位浮点值。它可以用来表示整数和分数。
字符串类型 string 一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式。
布尔类型 boolean 表示逻辑值:true 和 false。
数组类型 声明变量为数组。
元组 元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
枚举 enum 枚举类型用于定义数值集合。
null null 表示对象值缺失。
undefined undefined 用于初始化变量为一个未定义的值
never never never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。

示例:

// number:
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744;    // 八进制
let decLiteral: number = 6;    // 十进制
let hexLiteral: number = 0xf00d;    // 十六进制
// string
let name: string = "Runoob";
let years: number = 5;
let words: string = `您好,今年是 ${ name } 发布 ${ years + 1} 周年`;
// boolean
let flag: boolean = true;
// array
// 在元素类型后面加上[]
let arr: number[] = [1, 2];
// 声明元素类型既可以是数字又可以是字符串的数组
let arr: (number | string)[] = [1, "string"];
// 或者使用数组泛型
let arr: Array<number> = [1, 2];
//tuple
// 元组类型用来表示已知元素数量和类型的数组,
// 各元素的类型不必相同,对应位置的类型需要相同。
let x: [string, number];
x = ['Runoob', 1];    // 运行正常
x = [1, 'Runoob'];    // 报错
console.log(x[0]);    // 输出 Runoob
// enum
枚举类型用于定义数值集合。
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c);    // 输出 2
// void 用于标识方法返回值的类型,表示该方法没有返回值。
function hello(): void {
    alert("Hello Runoob");
}
Any 类型

任意值是 TypeScript 针对编程时类型不明确的变量使用的一种数据类型,它常用于以下三种情况。

1、变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查,示例代码如下:

let x: any = 1; // 数字类型
x = "I am who I am"; // 字符串类型
x = false; // 布尔类型

改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查,示例代码如下:

let x: any = 4;
x.ifItExists(); // 正确,ifItExists方法在运行时可能存在,但这里并不会检查
x.toFixed(); // 正确

定义存储各种类型数据的数组时,示例代码如下:

let arrayList: any[] = [1, false, "fine"];
arrayList[1] = 100;
Null 和 Undefined
null
  • 在 JavaScript 中 null 表示 “什么都没有”。
  • null 是一个只有一个值的特殊类型。表示一个空对象引用。
  • 用 typeof 检测 null 返回是 object。
undefined
  • JavaScript 中, undefined 是一个没有设置值的变量。
  • typeof 一个没有值的变量会返回 undefined。

Null 和 Undefined 是其他任何类型(包括 void)的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined。而在 TypeScript 中启用严格的空校验(–strictNullChecks)特性,就可以使得 null 和 undefined 只能被赋值给 void 或本身对应的类型,示例代码如下:

// 启用 --strictNullChecks
let x: number;
x = 1; // 运行正确
x = undefined;    // 运行错误
x = null;    // 运行错误
上面的例子中变量 x 只能是数字类型。如果一个类型可能出现 null 或 undefined, 可以用 | 来支持多种类型,示例代码如下:

// 启用 --strictNullChecks
let x: number | null | undefined;
x = 1; // 运行正确
x = undefined;    // 运行正确
x = null;    // 运行正确
never 类型

never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环),示例代码如下:

let x: never;
let y: number;

// 运行错误,数字类型不能转为 never 类型
x = 123;

// 运行正确,never 类型可以赋值给 never类型
x = (() => {
  throw new Error("exception");
})();

// 运行正确,never 类型可以赋值给 数字类型
y = (() => {
  throw new Error("exception");
})();

// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
  throw new Error(message);
}

// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
  while (true) {}
}

函数

我们需要为函数的参数添加类型声明

function addUser(firstName: string, lastName: string): string {
  return `${firstName} ${lastName}`;
}

如果有参数是可选的,可以通过 ? 来声明,但是只能放到参数列表的最后面

// lastName 是一个可选参数
function addUser(firstName: string, lastName?: string): string {
  return `${firstName} ${lastName || ""}`;
}

addUser("Jack");
addUser("Jack", "Lee");

如果返回值是可推导的,可以省略返回值的类型声明

function addUser(firstName: string, lastName: string) {
  // 从return值可以推导出,返回值是一个string类型
  return `${firstName} ${lastName}`;
}

接口

接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。
示例代码:

interface IPerson {
  firstName: string;
  lastName: string;
  sayHi: () => string;
}

var customer: IPerson = {
  firstName: "Tom",
  lastName: "Hanks",
  sayHi: (): string => {
    return "Hi there";
  },
};

console.log("Customer 对象 ");
console.log(customer.firstName);
console.log(customer.lastName);
console.log(customer.sayHi());

var employee: IPerson = {
  firstName: "Jim",
  lastName: "Blakes",
  sayHi: (): string => {
    return "Hello!!!";
  },
};

console.log("Employee  对象 ");
console.log(employee.firstName);
console.log(employee.lastName);

示例二:

interface IUser {
  name: string;
  age?: number; // 用?来声明一个可选属性
}

function addUser(user: IUser) {
  // do something
}
// 类型匹配IUser
addUser({ name: "Frank" });
// 类型匹配IUser
addUser({ name: "Frank", age: 21 });
// 类型不匹配IUser,可选属性age为number类型
addUser({ name: "Frank", age: "abc" });

泛型

软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

示例:identity 函数。 这个函数会返回任何传入它的值。

不用泛型的话,这个函数可能是下面这样:

function identity(arg: number): number {
  return arg;
}

或者,我们使用 any 类型来定义函数:

function identity(arg: any): any {
  return arg;
}

使用 any 类型会导致这个函数可以接收任何类型的 arg 参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。

因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。

function identity<T>(arg: T): T {
  return arg;
}

我们给 identity 添加了类型变量 T。 T 帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T 当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。

我们把这个版本的 identity 函数叫做泛型,因为它可以适用于多个类型。 不同于使用 any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。

我们定义了泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数:

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

这里我们明确的指定了 T 是 string 类型,并做为一个参数传给函数,使用了<>括起来而不是()。

第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定 T 的类型:

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

注意我们没必要使用尖括号(<>)来明确地传入类型;编译器可以查看 myString 的值,然后把 T 设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入 T 的类型,在一些复杂的情况下,这是可能出现的。

应用

泛型在前端的一个典型使用场景是对于网络请求返回结果的类型声明上面,如下面的例子,我们定义一个通用的网络请求方法,用户可以使用泛型来告诉 TS 这次网络请求返回的数据结构

interface IUser {
  name: string;
  age: number;
}

// T是类型变量,用来标注返回值的类型
async function request<T>(url: string) {
  let res = await fetch(url);
  let json: T = await res.json(); // res.json()返回的数据是any类型,这里我们把它主动标记为用户传入的类型T
  return json;
}

async function run() {
  // users是IUser[]类型
  let users = await request<IUser[]>("/api/user/list");
  console.log(users);
  // user是IUser类型
  let user = await request<IUser>("/api/user/find?id=1");
  console.log(user);
}

run();

Q.E.D.


永远自由,永远热爱