万字血书--TypeScript超详细入门

前言

作者我也还只是一个初学者,若下方的任何内容有误欢迎提出!


了解TypeScript

TypeScript是由微软进行开发和维护的一种开源的编程语言。TypeScript是JavaScript的严格语法超集,提供了可选的静态类型检查。

TypeScript的知名开发者有C#的首席架构师兼Delphi和Turbo Pascal的创始人——安德斯·海尔斯伯格。

TypeScript是为开发大型应用程序而设计的,且可转译成JavaScript。由于TypeScript是JavaScript的严格语法超集,因此任何现有的JavaScript程序都是合法的TypeScript程序。

TypeScript支持为现存JavaScript函数库添加类型信息的定义文件,方便其他程序像使用静态类型的TypeScript实体一样,使用现有程序库中的值,就像是C++的头文件可以描述目标文件(objectfile)的结构一样。有许多第三方头文件为热门函数库像是jQuery、MongoDB、Node.js和D3.js等提供定义文件。

TypeScript编译器本身也是用TypeScript编写,并被转译为JavaScript,以Apache许可证第二版发布。

TypeScript VS JavaScript

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。

TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。

相同点

  1. 基础语法: TypeScript 是 JavaScript 的超集,因此它们共享大部分语法和基本结构。你可以在 TypeScript 中使用 JavaScript 的所有功能和语法。

  2. 运行环境: TypeScript 代码在浏览器或 Node.js 中运行时,最终会被编译成 JavaScript,因此它们可以在相同的运行环境中执行。

  3. 开发工具支持: 两者都可以使用现代的开发工具和编辑器进行编写、调试和测试,比如 Visual Studio Code

不同点

  1. 类型系统:

    • JavaScript 是动态类型语言,在运行时确定变量的类型。
    • TypeScript 是静态类型语言,允许你在编译时指定变量的类型,从而提前捕获潜在的错误。这有助于提高代码的可维护性和可读性。
  2. 编译过程:

    • JavaScript 是解释性语言,直接在浏览器或 Node.js 中运行。
    • TypeScript 需要经过编译过程,将 TypeScript 代码转换为 JavaScript 代码才能运行。
  3. 类型注释:

    • JavaScript 不支持类型注释,所有变量的类型在运行时决定。
    • TypeScript 允许你为变量、函数参数和返回值添加类型注释,例如:
      function greet(name: string): string { return `Hello, ${name}`; }
  4. 接口和类型:

    • JavaScript 没有内置的接口和类型定义。
    • TypeScript 支持接口(interface)和类型别名(type),可以用来定义和检查对象的结构,例如:
      interface Person { name: string; age: number; } const person: Person = { name: "xuelang", age: 16 };
  5. 类和装饰器:

    • JavaScript 支持基本的类和继承,但不支持装饰器。
    • TypeScript 支持 ES6 的类语法,并且提供了装饰器(@decorator)功能,可以用于修改类和类成员的行为。
  6. 泛型:

    • JavaScript 不支持泛型。
    • TypeScript 支持泛型,可以用来创建可重用的组件和函数,例如:
      function identity<T>(arg: T): T { return arg; }
  7. 模块系统:

    • JavaScript 支持 ES6 的模块系统(importexport),但在旧版本的 JavaScript 中,需要使用 CommonJS 或 AMD。
    • TypeScript 支持 ES6 模块系统,并且与 CommonJS 和 AMD 模块兼容。
  8. 编译选项和配置:

    • JavaScript 不需要编译选项和配置。
    • TypeScript 使用 tsconfig.json 文件来配置编译选项和项目设置。

安装TypeScript

Windows 上安装 TypeScrip

安装Node.js

打开Node.js官网,下载Windows安装程序并且安装(通常为.msi文件)

安装完成后在cmd或者powershell中输入以下命令来检查Node.js何npm是否正确安装

node -v
npm -v

若安装正确会返回版本号(如图)

安装 TypeScript:

在命令提示符中运行以下命令安装 TypeScript:

npm install -g typescript

使用以下命令验证 TypeScript 是否安装成功:

tsc -v

Linux 上安装 TypeScript

安装 Node.js和npm:
sudo apt update
sudo apt install nodejs npm

检查 Node.js 和 npm 是否安装成功
 

node -v
npm -v
安装 TypeScript:

在终端中运行以下命令

sudo npm install -g typescript

验证是否安装成功

tsc -v

macOS 上安装 TypeScript

安装 Node.js:

打开Node.js官网,下载macOS安装程序(通常是.pkg文件),运行安装程序

安装完成后打开终端验证

node -v
npm -v
安装 TypeScript:

在终端中运行以下命令

sudo npm install -g typescript

验证是否安装成功

tsc -v

常用标点

分号 (;)
  • 作用: 结束语句。

  • 示例:

    let name = "Alice"; // 结束这个声明语句 name = "Bob"; // 结束这个赋值语句
  • 详细解释: TypeScript 中,分号用于表示一条语句的结束。虽然分号是可选的,但在某些情况下省略分号可能会导致语法错误或不明确的代码。因此,建议在每个语句后加上分号。

冒号 (:)
  • 作用: 用于定义变量的类型、函数参数的类型、返回值类型,以及区分三元运算符的条件部分和结果部分。

  • 示例:

    let age: number = 25; // age 是数字类型 function greet(name: string): void { // name 是字符串类型,函数没有返回值 console.log(`Hello, ${name}`); } let result = condition ? trueValue : falseValue; // 三元运算符
  • 详细解释: 冒号常用于类型注释,明确变量、函数参数、函数返回值的类型,以便在编译时进行类型检查。此外,在三元运算符中,冒号用于区分条件表达式的两个结果。

等号 (=)
  • 作用: 用于赋值,将右边的值赋给左边的变量。

  • 示例:

    let x = 5; // 将 5 赋值给变量 x let y = x + 2; // 将 x + 2 的结果赋值给 y
  • 详细解释: 在 TypeScript 中,等号用于将一个表达式的结果赋值给变量。左边的变量将会存储右边表达式的值。

双等号 (==) 和三等号 (===)
  • 作用: 用于比较两个值是否相等。== 进行值的比较(会进行类型转换),=== 进行严格的比较(不会进行类型转换)。

  • 示例:

    let isEqual = (5 == '5'); // true,因为类型转换后,两者相等 let isStrictEqual = (5 === '5'); // false,因为类型不同
  • 详细解释:

    • ==:会在比较前将不同类型的操作数转换为相同类型再进行比较,可能会导致意想不到的结果。
    • ===:不仅要求值相等,还要求类型相同,更加安全和可预测。
花括号 ({})
  • 作用: 用于表示代码块,包含一组相关的语句。常用于函数、条件判断、循环等语句中。

  • 示例:

    function greet() { console.log("Hello!"); // 这个代码块属于函数 greet } if (x > 10) { console.log("x is greater than 10"); // 这个代码块属于 if 语句 }
  • 详细解释: 花括号用于将多条语句组合在一起,形成一个语句块。在函数、循环、条件判断等场景中,语句块确保代码结构的清晰。

括号 (())
  • 作用: 用于调用函数或包裹表达式。

  • 示例:

    greet(); // 调用函数 greet let result = (x + y) * z; // 括号包裹表达式,改变运算优先级
  • 详细解释:

    • 在函数调用中,括号用于传递参数并执行函数。
    • 在数学运算中,括号用于明确计算顺序,确保表达式按预期进行。
方括号 ([])
  • 作用: 用于表示数组或访问数组中的元素。

  • 示例:

    let numbers = [1, 2, 3]; // 定义一个数组 let firstNumber = numbers[0]; // 访问数组的第一个元素
  • 详细解释:

    • 方括号用于定义数组并存储多个值。
    • 通过方括号中的索引值,可以访问数组中特定位置的元素。
箭头 (=>)
  • 作用: 用于定义箭头函数,提供一种简洁的函数表达方式。

  • 示例:

    let add = (a: number, b: number): number => a + b; // 定义一个箭头函数

    详细解释:

    • 箭头函数是 TypeScript 和 JavaScript 中的一种简洁的函数表达方式,特别适用于定义匿名函数。
    • 它不会绑定自己的 this,对于处理回调函数非常有用。
引号 ("", '')
  • 作用: 用于定义字符串。

  • 示例

  • ​​​​​​​let greeting = "Hello, world!"; // 使用双引号 let farewell = 'Goodbye!'; // 使用单引号
  • 详细解释:

    • 引号可以是双引号或单引号,两者功能相同,但不能混用。
    • 在字符串中嵌入引号时,可以使用另一种引号类型,避免转义。
反引号 (`)
  • 作用: 用于定义模板字符串,允许在字符串中插入变量或表达式。

  • 示例:

    let name = "xuelang"; let message = `Hello, ${name}!`; // 插入变量 name
  • 详细解释:

    • 模板字符串使用反引号定义,支持多行字符串,并允许在 ${} 中插入变量或表达式的值,增强了字符串的灵活性。
逗号 (,)
  • 作用: 用于分隔列表中的多个元素,如函数参数、数组元素、对象属性等。

  • 示例:

    let colors = ["red", "green", "blue"]; // 数组中的元素由逗号分隔 function multiply(x: number, y: number): number { // 函数参数由逗号分隔 return x * y; }
  • 详细解释:

    • 逗号用于分隔同一结构中的多个元素,保持代码的整齐和可读性。
点 (.)
  • 作用: 用于访问对象的属性或方法,以及使用 TypeScript 中的泛型类型。

  • 示例:

    let person = { name: "Alice", age: 30 }; console.log(person.name); // 访问对象的属性
  • 详细解释:

    • 点操作符用于访问对象的属性或方法。
    • 在泛型类型中,点操作符用于引用特定的类型成员。
尖括号 (<>)
  • 作用: 用于定义泛型类型,使函数、类、接口更加通用。

  • 示例:

    function identity<T>(arg: T): T { // 定义泛型函数 return arg; }
  • 详细解释:

    • 尖括号内的泛型类型可以是任意类型,允许函数或类处理多种类型的数据,而无需在编写时指定具体类型。
问号 (?)
  • 作用: 用于可选属性、可选参数或类型保护。

  • 示例:

    interface Person { name: string; age?: number; // age 是可选属性 } function greet(name: string, age?: number): void { // age 是可选参数 if (age) { console.log(`Hello, ${name}. You are ${age} years old.`); } else { console.log(`Hello, ${name}.`); } }
  • 详细解释:

    • 可选属性和可选参数允许在使用对象或函数时省略某些值。
    • 问号在类型保护中表示对象属性或参数可能不存在,增强了代码的灵活性。
双斜线 (//) 和斜杠星号 (/ ... /)
  • 作用: 用于编写单行和多行注释,注释内容不会被执行。

  • 示例:

    // 这是单行注释 
    let x = 10;
    /* 这是多行注释
    可以有多行内容*/ 
    let y = 20;
  • 详细解释:

    • 注释用于解释代码,便于自己或他人阅读。
    • 单行注释使用 //,多行注释使用 /* ... */
波浪号 (~)
  • 作用: 按位非运算符,将每个位都取反。

  • 示例:

    let x = ~10; // 按位取反
  • 详细解释:

    • 波浪号用于按位操作,将数值的每个位都取反。
    • 这个操作符在现代编程中较少使用,通常在底层或特定场景下使用。
感叹号 (!)
  • 作用: 用于表示逻辑非运算或非空断言。

  • 示例:

    let isTrue = !false; // 逻辑非运算 let name: string | null = "Alice"; console.log(name!); // 非空断言,告诉编译器 name 不为 null
  • 详细解释:

    • 逻辑非运算将布尔值取反,truefalsefalsetrue
    • 非空断言用于表示某个值不会是 nullundefined,避免编译错误。

TypeScript 基本语法

模块

TypeScript 支持模块化编程,这可以通过 exportimport 关键字来实现。模块帮助你将代码组织到不同的文件中,使其更加模块化和可重用。

// math.ts
export function add(x: number, y: number): number {
  return x + y;
}

// app.ts
import { add } from './math';

console.log(add(2, 3)); // 5

函数

TypeScript 中的函数声明语法和 JavaScript 相似,但可以增加类型注解以确保函数参数和返回值的类型安全。

函数声明:
function greet(name: string): string { return `Hello, ${name}`; }
函数表达式:
const multiply = (x: number, y: number): number => x * y;
可选参数和默认参数:
function greet(name: string, age?: number): string {
  if (age !== undefined) {
    return `Hello, ${name}. You are ${age} years old.`;
  } else {
    return `Hello, ${name}.`;
  }
}

function log(message: string = "Default message"): void {
  console.log(message);
}

变量

TypeScript 允许你声明变量并指定类型,确保变量使用的类型正确。

声明变量:
let isActive: boolean = true;
let age: number = 25;
let name: string = "John";
数组和元组:
let numbers: number[] = [1, 2, 3];
let tuple: [string, number] = ["Hello", 10];

语句和表达式

语句是执行操作的代码段,而表达式是产生值的代码段。

语句:
let x = 10;
if (x > 5) {
  console.log("x is greater than 5");
}
表达式:
let y = 2 * 5; // 这是一个表达式

注释

注释可以帮助你解释代码。TypeScript 支持两种注释类型:

单行注释:
// 这是一条单行注释
//这又是一条单行注释
//这还是一条单行注释
多行注释:
/*
这是一条多行注释
它可以包含很多很多很多很多内容
最关键的一点就是可以换行
awa
*/

面向对象

TypeScript 的面向对象编程支持包括类、继承、接口和访问修饰符(public, private, protected)。

类和对象:
class Person {
  private name: string;

  constructor(name: string) {
    this.name = name;
  }

  public greet(): void {
    console.log(`Hello, my name is ${this.name}`);
  }
}

const person = new Person("Alice");
person.greet(); // Hello, my name is Alice
继承:
class Animal {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }

  public speak(): void {
    console.log(`${this.name} makes a sound.`);
  }
}

class Dog extends Animal {
  public bark(): void {
    console.log(`${this.name} barks.`);
  }
}

const dog = new Dog("Buddy");
dog.speak(); // Buddy makes a sound.
dog.bark();  // Buddy barks.
接口:
interface Person {
  name: string;
  age?: number; // Optional property
}

function greet(person: Person): string {
  return `Hello, ${person.name}`;
}

const user: Person = { name: "John" };
console.log(greet(user)); // Hello, John
枚举:
interface Person {
  name: string;
  age?: number; // Optional property
}

function greet(person: Person): string {
  return `Hello, ${person.name}`;
}

const user: Person = { name: "John" };
console.log(greet(user)); // Hello, John
泛型:
function identity<T>(arg: T): T {
  return arg;
}

let output = identity<string>("Hello");
命名空间:
namespace Utility {
  export function log(message: string): void {
    console.log(message);
  }
}

Utility.log("This is a message"); // This is a message
元组:
let pair: [string, number] = ["age", 25];
Async/Await:
async function fetchData(): Promise<string> {
  return "Data fetched";
}

fetchData().then(data => console.log(data));

TypeScript 基本类型

在TypeScript中,提供了一下基本数据类型

  • 布尔类型(boolean)

  • 数据类型(number)

  • 字符串类型(string)

  • 数组类型(array)

  • 元组类型(tuple)

  • 枚举类型(enum)

  • 任意值类型(any)

  • null 和 undefined

  • void 类型

  • never 类型

其中元组、枚举、任意值、void类型和 never类型是TypeScript有别与JavaScript的特有类型。
在TypeScript中声明变量,需要加上类型声明,例如boolean和string等。通过静态类型约束,在编译时执行类型检查,可以避免一些类型混用的低级错误。

布尔类型

布尔类型是最简单的数据类型,只有true和false两种值。也就是说如果赋值为非boolean的其他类型值,编译时会抛出错误。

let flag: boolean = true;
flag = 1; // 报错
数据类型

在TyopeScript中,数字都是浮点型。TypeScript同时支持二进制、八进制、十进制和十六进制字面量,示例代码如下:

let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744;    // 八进制
let decLiteral: number = 6;    // 十进制
let hexLiteral: number = 0xf00d;    // 十六进制
字符串类型

TypeScript支持使用单引号(')或双引号(")来表示字符串类型。还支持使用模板字符串反引号(`)来定义多行文本和内嵌表达式。使用${ expr }的形式嵌入变量或表达式,在处理拼接字符串的时候很有用,示例如下:

let name: string = "Angular";
let years: number = 5;
let words: string = `您好,今年是 ${ name } 发布 ${ years + 1} 周年`;
数组类型

TypeScript数组的操作类似与JavaScript中数组的操作,TypeScript有两种数组定义方式,示例代码如下:

// 在元素类型后面加上[]
let arr: number[] = [1, 2];

// 或者使用数组泛型
let arr: Array<number> = [1, 2];
元组类型

元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同。下面定义了一组值分别为字符串和数字类型的元组,示例代码如下:

let x: [string, number];
x = ['Angular', 25];    // 运行正常
x = [25, 'Angular'];    // 报错
console.log(x[0]);    // 输出 Angular
枚举类型

枚举是一个可被命名的整型常数的集合,枚举类型为集合成员赋予有意义的名称,增强可读性,示例代码如下:

enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c);    // 输出 2

枚举默认下标是0,可以手动修改默认下标值,示例代码如下:

enum Color {Red = 2, Blue, Green = 6};
let c: Color = Color.Blue;
console.log(c);    // 输出:3
任意值类型

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

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

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 和 undefined 是其它类型的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 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;    // 运行正确
void 类型

在 TypeScript 中,使用 void 表示没有任何类型。 例如一个函数没有返回值时,意味着返回值类型是 void,示例代码如下:

function hello(): void {
    alert("Hello Angular");
}

对于可忽略返回值的回调函数来说,使用 void 类型会比任意值类型更安全一些,示例代码如下:

function func(foo:() => void) {
    let f = foo();    // 使用函数 foo 的返回值
    f.doSth();    // 报错, void 类型不存在 doSth() 方法, 此时换成任意值类型都不回报错
}
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) {}
}

TypeScript变量声明

变量是一种使用方便的占位符,用于引用计算机内存地址。

我们可以把变量看做存储数据的容器。

TypeScript 变量的命名规则:

  • 变量名称可以包含数字和字母。

  • 除了下划线 _ 和美元 $ 符号外,不能包含其他特殊字符,包括空格。

  • 变量名不能以数字开头。

变量使用前必须先声明,我们可以使用 var 来声明变量。

我们可以使用以下四种方式来声明变量:

声明变量的类型及初始值:

var [变量名] : [类型] = 值;

例如:

var uname:string = "xuelang";

声明变量的类型,但没有初始值,变量值会设置为 undefined:

var [变量名] : [类型];

例如:

var uname:string;

声明变量并初始值,但不设置类型,该变量可以是任意类型:

var [变量名] = 值;

例如:

var uname = "xuelang";

声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined:

var [变量名];

例如:

var uname;

 实战:

// 定义学生对象类型
type Student = {
    name: string;
    age: number;
    grades: number[];
    isPassed: boolean;
};

// 创建一个学生对象
let student: Student = {
    name: "Alice",
    age: 20,
    grades: [85, 78, 92, 88],
    isPassed: false
};

// 计算学生的平均成绩
function calculateAverage(grades: number[]): number {
    let total = 0;
    for (let grade of grades) {
        total += grade;
    }
    return total / grades.length;
}

// 判断学生是否通过考试
function checkPassStatus(averageGrade: number): boolean {
    return averageGrade >= 60;
}

// 获取学生的平均成绩
let averageGrade: number = calculateAverage(student.grades);

// 设置学生的通过状态
student.isPassed = checkPassStatus(averageGrade);

// 输出学生信息
console.log("学生名字: " + student.name);
console.log("学生年龄: " + student.age);
console.log("学生成绩: " + student.grades.join(", "));
console.log("学生平均成绩: " + averageGrade);
console.log("学生是否通过: " + (student.isPassed ? "通过" : "未通过"));

输出:

学生名字: Alice
学生年龄: 20
学生成绩: 85, 78, 92, 88
学生平均成绩: 85.75
学生是否通过: 通过

注意:变量不要使用 name,否则会与 DOM 中的全局 window 对象下的 name 属性出现了重名

TypeScript 强类型的特性意味着在声明变量时,如果类型和初始值不匹配,编译器会报错

如果不指定类型,TypeScript 会根据初始值推断类型(类型推断)。

类型断言(Type Assertion)

类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型。

语法格式:

<类型>值

或者(在JSX语法中必须使用as语法)

值 as 类型

实例:

var str = '1' 
var str2:number = <number> <any> str   //str、str2 是 string 类型
console.log(str2)

TypeScript 是怎么确定单个断言是否足够的?

TypeScript 在处理类型断言时,并不是随意允许任何类型之间的转换。它有一些基本的规则来决定何时可以进行类型断言,并确保断言的安全性。具体来说,当你尝试在类型 S 和类型 T 之间进行断言时,TypeScript 主要依据以下规则来判断断言是否足够:

S 是 T 的子类型

如果类型 S 是类型 T 的子类型,那么你可以将 S 类型的值断言为 T 类型。因为 S 类型的值可以安全地用作 T 类型。

T 是 S 的子类型

如果类型 T 是类型 S 的子类型,那么你可以将 T 类型的值断言为 S 类型。因为 T 类型的值可以安全地用作 S 类型。

any 类型

any 类型是一个万能类型,可以被断言为任何其他类型。同样,任何类型的值也可以被断言为 any。

实例:
interface Animal {
    name: string;
    move: () => void;
}

interface Bird extends Animal {
    fly: () => void;
}

let animal: Animal = { name: "Elephant", move: () => console.log("Moving") };
let bird: Bird = { name: "Sparrow", move: () => console.log("Moving"), fly: () => console.log("Flying") };

// Bird 是 Animal 的子类型
let animalAsBird: Bird = animal as Bird; // 合法,但不安全,fly 属性不存在

// Animal 是 Bird 的父类型
let birdAsAnimal: Animal = bird as Animal; // 合法且安全

在上述例子中:

birdAsAnimal:因为 Bird 是 Animal 的子类型(Bird extends Animal),所以可以将 Bird 类型断言为 Animal 类型,这是完全安全的。

animalAsBird:虽然可以将 Animal 类型断言为 Bird 类型,但这可能是不安全的,因为 Animal 类型的对象可能不具有 Bird 类型的 fly 方法。

单个断言是否足够

当 S 和 T 之间有子类型关系时,单个断言通常是足够的。例如:

Bird 是 Animal 的子类型,你可以将 Bird 断言为 Animal,这是安全的,编译器允许这种断言。

反过来,如果你要将一个 Animal 断言为 Bird,尽管编译器允许这种断言(因为它没有足够的信息来拒绝),但运行时可能会失败(因为 Animal 不一定有 Bird 所需的属性和方法)。

断言的风险和 any

当类型之间没有明确的子类型关系时,TypeScript 可能会要求你进行两次断言,即通过 any 进行断言。这样做虽然可以绕过类型检查,但丧失了类型安全性。

例如:

let str: any = "this is a string";
let num: number = str as any as number; // 不安全的双重断言

类型推断

当类型没有给出时,TypeScript 编译器利用类型推断来推断类型。

如果由于缺乏声明而不能推断出类型,那么它的类型被视作默认的动态 any 类型。

var num = 2;    // 类型推断为 number
console.log("num 变量的值为 "+num); 
num = "12";    // 编译错误
console.log(num);

第一行代码声明了变量 num 并=设置初始值为 2。 注意变量声明没有指定类型。因此,程序使用类型推断来确定变量的数据类型,第一次赋值为 2,num 设置为 number 类型。

第三行代码,当我们再次为变量设置字符串类型的值时,这时编译会错误。因为变量已经设置为了 number 类型。

error TS2322: Type '"12"' is not assignable to type 'number'.

变量作用域

在 TypeScript 中使用 var 声明的变量是函数作用域(Function Scope),而使用 let 或 const 声明的变量是块作用域(Block Scope)。

块作用域意味着变量只能在其声明的块内访问,适合在循环和条件语句中使用。

变量作用域指定了变量定义的位置。

程序中变量的可用性由变量作用域决定。

TypeScript 有以下几种作用域:

  • 全局作用域 − 全局变量定义在程序结构的外部,它可以在你代码的任何位置使用。

  • 类作用域 − 这个变量也可以称为 字段。类变量声明在一个类里头,但在类的方法外面。 该变量可以通过类的对象来访问。类变量也可以是静态的,静态的变量可以通过类名直接访问。

  • 局部作用域 − 局部变量,局部变量只能在声明它的一个代码块(如:方法)中使用。

以下实例说明了三种作用域的使用:

var global_num = 12          // 全局变量
class Numbers { 
   num_val = 13;             // 实例变量
   static sval = 10;         // 静态变量
   
   storeNum():void { 
      var local_num = 14;    // 局部变量
   } 
} 
console.log("全局变量为: "+global_num)  
console.log(Numbers.sval)   // 静态变量
var obj = new Numbers(); 
console.log("实例变量: "+obj.num_val)

以上代码使用 tsc 命令编译为 JavaScript 代码为:

var global_num = 12; // 全局变量
var Numbers = /** @class */ (function () {
    function Numbers() {
        this.num_val = 13; // 实例变量
    }
    Numbers.prototype.storeNum = function () {
        var local_num = 14; // 局部变量
    };
    Numbers.sval = 10; // 静态变量
    return Numbers;
}());
console.log("全局变量为: " + global_num);
console.log(Numbers.sval); // 静态变量
var obj = new Numbers();
console.log("实例变量: " + obj.num_val);

执行以上 JavaScript 代码,输出结果为:

全局变量为: 12
10
实例变量: 13

如果我们在方法外部调用局部变量 local_num,会报错:

error TS2322: Could not find symbol 'local_num'.

TypeScript 运算符

在 TypeScript 中,运算符用于对变量和数据执行操作。TypeScript 的运算符与 JavaScript 的大部分运算符相同,但由于 TypeScript 是静态类型的,因此在类型检查和处理上会有更多的安全性。下面是所有 TypeScript 运算符的分类、定义和用法。

算术运算符

这些运算符用于执行数学运算。

运算符名称例子结果
+加法5 + 38
-减法5 - 32
*乘法5 * 315
/除法6 / 32
%取余数5 % 32
++自增let a = 5; a++a = 6
--自减let a = 5; a--a = 4

赋值运算符

这些运算符用于给变量赋值。

运算符名称例子结果
=赋值a = 5a = 5
+=加赋值a += 2a = a + 2
-=减赋值a -= 2a = a - 2
*=乘赋值a *= 2a = a * 2
/=除赋值a /= 2a = a / 2
%=取余赋值a %= 2a = a % 2
**=指数赋值a **= 2a = a ** 2

比较运算符

这些运算符用于比较两个值,并返回一个布尔值。

运算符名称例子结果
==相等5 == '5'true
===严格相等(值和类型相等)5 === '5'false
!=不相等5 != '5'false
!==严格不相等(值或类型不相等)5 !== '5'true
>大于5 > 3true
<小于5 < 3false
>=大于等于5 >= 3true
<=小于等于5 <= 3false

逻辑运算符

这些运算符用于逻辑运算,通常用于布尔值。

运算符名称例子结果
&&逻辑与true && falsefalse
||逻辑或`true
!逻辑非!truefalse

位运算符

这些运算符用于按位处理数字。

运算符名称例子结果
&按位与5 & 31
|按位或5 | 37
^按位异或5 ^ 36
~按位非~5-6
<<左移位5 << 110
>>右移位5 >> 12
>>>无符号右移位5 >>> 12

条件(三元)运算符

条件运算符用于根据条件表达式返回值。

运算符名称例子结果
? :条件运算符true ? 'yes' : 'no''yes'

字符串运算符

这些运算符用于字符串操作。

运算符名称例子结果
+字符串连接'Hello ' + 'World''Hello World'

其他运算符

逗号运算符 (,): 用于在单个语句中求值多个表达式,返回最后一个表达式的值。例子:let a = (1, 2, 3);,a 的值为 3。


类型断言运算符 (as 和 <type>): 用于显式地告诉编译器某个变量的类型。例子:let someValue: any = "this is a string"; let strLength: number = (someValue as string).length;


可选链运算符 (?.): 用于在对象的深层属性或方法调用可能未定义的情况下安全访问。例子:let value = obj?.prop;


空值合并运算符 (??): 返回第一个不为 null 或 undefined 的操作数。例子:let a = null ?? 'default';,a 的值为 'default'。

解构赋值

解构赋值不是一个单一的运算符,但是一种用于从数组或对象中提取值并赋给变量的语法。

例子结果
let [a, b] = [1, 2];a = 1, b = 2
let {x, y} = {x: 1, y: 2};x = 1, y = 2

展开运算符 (...)

用于在数组、对象、函数参数中展开元素。

例子结果
let arr = [1, 2, ...[3, 4]];arr = [1, 2, 3, 4]
let obj = {...{x: 1, y: 2}, z: 3};obj = {x: 1, y: 2, z: 3}

TypeScript 条件语句

条件语句用于基于不同的条件来执行不同的动作。

TypeScript 条件语句是通过一条或多条语句的执行结果(True 或 False)来决定执行的代码块。

可以通过下图来简单了解条件语句的执行过程:

if 语句

if 语句用于在条件为 true 时执行一段代码。

流程图:

语法:

if (condition) {
  // code to be executed if condition is true
}

实例:

let num: number = 10;

if (num > 5) {
  console.log("Number is greater than 5");
}

if-else 语句

if-else 语句用于在条件为 true 时执行一段代码,在条件为 false 时执行另一段代码。

流程图:

语法:

if (condition) {
  // code to be executed if condition is true
} else {
  // code to be executed if condition is false
}

实例:

let num: number = 3;

if (num > 5) {
  console.log("Number is greater than 5");
} else {
  console.log("Number is 5 or less");
}

else if 语句

else if 语句用于在初始条件为 false 时测试另一个条件。

语法:

if (condition1) {
  // code to be executed if condition1 is true
} else if (condition2) {
  // code to be executed if condition2 is true
} else {
  // code to be executed if all conditions are false
}

实例:

let num: number = 5;

if (num > 10) {
  console.log("Number is greater than 10");
} else if (num > 5) {
  console.log("Number is greater than 5 but less than or equal to 10");
} else {
  console.log("Number is 5 or less");
}

switch 语句

switch 语句用于在多个可能的情况下选择其中之一执行。

流程图:

语法:

switch (expression) {
  case value1:
    // code to be executed if expression === value1
    break;
  case value2:
    // code to be executed if expression === value2
    break;
  // ...
  default:
    // code to be executed if no cases match
}

实例:

let fruit: string = "apple";

switch (fruit) {
  case "banana":
    console.log("Banana is selected");
    break;
  case "apple":
    console.log("Apple is selected");
    break;
  case "orange":
    console.log("Orange is selected");
    break;
  default:
    console.log("No matching fruit found");
}

三元运算符 (?:)

三元运算符是 if-else 的简写形式,用于在条件为 truefalse 时返回不同的值。

语法:

condition ? expr1 : expr2

实例:

let num: number = 4;
let result: string = num > 5 ? "Greater than 5" : "5 or less";
console.log(result);

TypeScript 循环

有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。

编程语言提供了更为复杂执行路径的多种控制结构。

循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图:

图片来源runoob.com

for 循环

for 循环用于在确定的次数内重复执行代码块。

语法:

for ( init; condition; increment ){
    statement(s);
}

下面是 for 循环的控制流程解析:

  1. init 会首先被执行,且只会执行一次。这一步允许您声明并初始化任何循环控制变量。您也可以不在这里写任何语句,只要有一个分号出现即可。
  2. 接下来,会判断 condition。如果为 true,则执行循环主体。如果为 false,则不执行循环主体,且控制流会跳转到紧接着 for 循环的下一条语句。
  3. 在执行完 for 循环主体后,控制流会跳回上面的 increment 语句。该语句允许您更新循环控制变量。该语句可以留空,只要在条件后有一个分号出现即可。
  4. 条件再次被判断。如果为 true,则执行循环,这个过程会不断重复(循环主体,然后增加步值,再然后重新判断条件)。在条件变为 false 时,for 循环终止。

在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。

condition 可以是任意的表达式,当条件为 true 时执行循环,当条件为 false 时,退出循环。

流程图:

实例:

for (let i: number = 0; i < 5; i++) {
  console.log(i);
}

while 循环

while 循环在条件为 true 时重复执行代码块。条件在每次迭代之前检查。

语法:

while(condition)
{
   statement(s);
}

在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。

condition 可以是任意的表达式,当条件为 true 时执行循环。 当条件为 false 时,程序流将退出循环。

流程图:

实例:

var num:number = 5; 
var factorial:number = 1; 
 
while(num >=1) { 
    factorial = factorial * num; 
    num--; 
} 
console.log("5 的阶乘为:"+factorial);

for...in 循环

for...in 循环用于遍历对象的可枚举属性。对于数组,for...in 循环遍历的是数组的索引(键),而不是数组的值。如果你想遍历数组的值,通常使用 for...of 循环或传统的 for 循环会更合适。

  • for...in 循环遍历的是对象的可枚举属性,而不仅仅是对象自身的属性,还包括原型链上的属性。因此,通常建议使用 Object.hasOwnProperty() 方法来过滤掉原型链上的属性。
  • 对于数组,如果你需要遍历数组的值,建议使用 for...of 循环,因为它直接遍历数组的值,而不是索引。

语法:

for (let key in object) {
  // code to be executed
}

实例:

const person = {
  name: "Alice",
  age: 25,
  city: "New York"
};

for (let key in person) {
  console.log(`${key}: ${person[key]}`);
}

for…of 、forEach、every 和 some 循环

此外,TypeScript 还支持 for…of 、forEach、every 和 some 循环。

for...of 语句创建一个循环来迭代可迭代的对象。在 ES6 中引入的 for...of 循环,以替代 for...in 和 forEach() ,并支持新的迭代协议。for...of 允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等。

for...of 循环

for...of 循环用于遍历可迭代对象的值,例如数组或字符串。

语法:

for (let item of iterable) {
  // code to be executed
}

实例:

const numbers: number[] = [1, 2, 3, 4, 5];

for (let num of numbers) {
  console.log(num);
}
forEach 循环

forEach 是数组方法,它允许你对数组的每个元素执行一次提供的函数。它是 for 循环的简洁替代方式,尤其在处理数组时非常方便。forEach 不会改变原数组。

语法:

array.forEach(callback(currentValue, index, array), thisArg);

callback:在数组每个元素上执行的函数,包含以下参数:
currentValue:当前正在处理的元素的值。
index(可选):当前正在处理的元素的索引。
array(可选):正在遍历的数组。
thisArg(可选):当执行回调函数时,用作 this 的值。

示例:

const numbers: number[] = [1, 2, 3, 4, 5];

numbers.forEach((num) => {
  console.log(num);
});

forEach 无法通过 break、continue 或 return 语句来提前退出循环。
forEach 通常用于需要对数组的每个元素执行操作但不需要提前退出的场景。

every 循环

every 方法用于测试数组中的所有元素是否都通过了指定函数的测试。如果数组中的所有元素都满足条件,则返回 true;如果有任何一个元素不满足条件,则返回 false。一旦检测到一个元素不满足条件,every 方法就会立即返回 false,不会继续遍历后续元素。

array.every(callback(element, index, array), thisArg);

callback:在数组每个元素上执行的函数,包含以下参数:
element:当前正在处理的元素的值。
index(可选):当前正在处理的元素的索引。
array(可选):正在遍历的数组。
thisArg(可选):当执行回调函数时,用作 this 的值。

示例:

const numbers: number[] = [1, 2, 3, 4, 5];

const allPositive = numbers.every((num) => num > 0);

console.log(allPositive); // 输出: true

在这个示例中,every 方法测试 numbers 数组中的每个元素是否都大于 0。由于所有元素都满足条件,所以返回 true

every 也可以用于更复杂的数据结构,例如对象数组。

const users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 20 },
  { name: "Charlie", age: 35 }
];

const allAdults = users.every(user => user.age >= 18);

console.log(allAdults); // 输出: true

do-while 循环

do-while 循环类似于 while 循环,但它保证至少执行一次循环体,因为条件是在循环体之后检查的。

语法:

do
{
   statement(s);
}while( condition );

请注意,条件表达式出现在循环的尾部,所以循环中的 statement(s) 会在条件被测试之前至少执行一次。

如果条件为 true,控制流会跳转回上面的 do,然后重新执行循环中的 statement(s)。这个过程会不断重复,直到给定条件变为 false 为止。

流程图:

示例:

let i: number = 0;

do {
  console.log(i);
  i++;
} while (i < 5);

break 语句

break 语句在 TypeScript 中用于立即终止循环或 switch 语句的执行,并跳出该结构。它非常有用,尤其是在循环中,当你找到了所需的结果或条件满足时,可以使用 break 语句来停止进一步的执行。

break 语句有以下两种用法:

  1. 当 break 语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。
  2. 它可用于终止 switch 语句中的一个 case。

如果您使用的是嵌套循环(即一个循环内嵌套另一个循环),break 语句会停止执行最内层的循环,然后开始执行该块之后的下一行代码。

语法:

break;

流程图:

for循环中的break
for (let i: number = 0; i < 10; i++) {
  if (i === 5) {
    break; // 当 i 等于 5 时,跳出循环
  }
  console.log(i);
}

输出:

0
1
2
3
4

在这个示例中,当 i 等于 5 时,break 语句终止了循环,因此 5 及其后的值不会被打印。

while中的break
let i: number = 0;

while (i < 10) {
  if (i === 5) {
    break; // 当 i 等于 5 时,跳出循环
  }
  console.log(i);
  i++;
}

输出:

0
1
2
3
4

for 循环的示例类似,当 i 等于 5 时,break 语句终止了循环。

switch中的break

switch 语句中,break 用于在一个 case 语句块执行完后停止执行其他 case 语句。否则,switch 语句会“贯穿”到下一个 case 语句。

const fruit: string = "apple";

switch (fruit) {
  case "banana":
    console.log("Banana is selected");
    break;
  case "apple":
    console.log("Apple is selected");
    break; // 跳出 switch,不会执行后面的 case 语句
  case "orange":
    console.log("Orange is selected");
    break;
  default:
    console.log("No matching fruit found");
}

输出

Apple is selected

在这个示例中,当 fruit 的值为 "apple" 时,switch 语句匹配到相应的 case,并在执行完 console.log("Apple is selected"); 后,break 语句阻止了后续 case 的执行。

在 switch 中:break 用于在执行完一个 case 后阻止其他 case 的执行,避免“贯穿”现象。

continue 语句

continue 语句用于跳过循环中的当前迭代,直接进入下一次迭代。在循环中遇到 continue 语句时,程序会跳过其余的循环体代码,并重新开始下一次循环。

for中的continue
for (let i: number = 0; i < 10; i++) {
  if (i % 2 === 0) {
    continue; // 如果 i 是偶数,跳过后续代码,直接进入下一次迭代
  }
  console.log(i);
}

输出:

1
3
5
7
9

在这个示例中,continue 语句在 i 是偶数时跳过 console.log(i);,因此只打印了奇数。

while中的continue
let i: number = 0;

while (i < 10) {
  if (i % 2 === 0) {
    i++;
    continue; // 如果 i 是偶数,跳过后续代码,直接进入下一次迭代
  }
  console.log(i);
  i++;
}

输出:

1
3
5
7
9

for 循环的示例类似,continue 语句跳过了偶数的输出,因此只打印了奇数。

do-while中的continue
let i: number = 0;

do {
  if (i % 2 === 0) {
    i++;
    continue; // 如果 i 是偶数,跳过后续代码,直接进入下一次迭代
  }
  console.log(i);
  i++;
} while (i < 10);

输出:

1
3
5
7
9

无限循环

无限循环就是一直在运行不会停止的循环。 for 和 while 循环都可以创建无限循环。

for 创建无限循环语法格式:

for(;;) { 
   // 语句
}

while 创建无限循环语法格式:

while(true) { 
   console.log("这段代码会不停的执行") 
}

TypeScript 函数

函数是一组一起执行一个任务的语句。

您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。

函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。

函数定义

在 TypeScript 中,函数定义的基本语法如下:

function functionName(parameter1: type1, parameter2: type2): returnType {
    // function body
    return value;
}

函数调用

调用函数时,你需要传递与函数定义中相匹配的参数:

functionName(arg1, arg2);

函数返回

函数可以返回一个值,也可以没有返回值(即 void)。如果函数有返回值,类型必须与返回值类型匹配。

function add(a: number, b: number): number {
    return a + b;
}

function logMessage(message: string): void {
    console.log(message);
}

参数

必需参数

这是最基本的参数类型,调用函数时必须提供这些参数。

function greet(name: string): string {
    return `Hello, ${name}!`;
}

console.log(greet("Alice")); // 输出: Hello, Alice!
可选参数

可选参数在函数定义时可以加上 ?,调用时可以省略这些参数。

function greet(name: string, age?: number): string {
    if (age) {
        return `Hello, ${name}. You are ${age} years old.`;
    } else {
        return `Hello, ${name}!`;
    }
}

console.log(greet("Alice")); // 输出: Hello, Alice!
console.log(greet("Alice", 30)); // 输出: Hello, Alice. You are 30 years old.
默认参数

默认参数允许在函数定义时为参数指定默认值,如果调用时未提供这些参数,则使用默认值。

function greet(name: string, age: number = 18): string {
    return `Hello, ${name}. You are ${age} years old.`;
}

console.log(greet("Alice")); // 输出: Hello, Alice. You are 18 years old.
console.log(greet("Alice", 30)); // 输出: Hello, Alice. You are 30 years old.
剩余参数

剩余参数用于处理不确定数量的参数,它将所有剩余参数作为一个数组传递。

function sum(...numbers: number[]): number {
    return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3)); // 输出: 6
console.log(sum(1, 2, 3, 4, 5)); // 输出: 15

函数类型

TypeScript 允许定义函数类型以声明函数的类型签名。

// 定义一个函数类型
type MathOperation = (x: number, y: number) => number;

const add: MathOperation = (x, y) => x + y;

console.log(add(10, 20)); // 输出: 30

箭头函数

箭头函数是简写函数定义的一种方式,没有 this 绑定。

const add = (x: number, y: number): number => x + y;

console.log(add(10, 20)); // 输出: 30

函数重载

函数重载允许一个函数有多个签名,每个签名可以具有不同的参数和返回类型。

function greet(person: string): string;
function greet(person: string, age: number): string;
function greet(person: string, age?: number): string {
    if (age !== undefined) {
        return `Hello, ${person}. You are ${age} years old.`;
    } else {
        return `Hello, ${person}!`;
    }
}

console.log(greet("Alice")); // 输出: Hello, Alice!
console.log(greet("Alice", 30)); // 输出: Hello, Alice. You are 30 years old.

TypeScript Number

TypeScript 与 JavaScript 类似,支持 Number 对象。

Number 对象是原始数值的包装对象。

var num = new Number(value);

注意: 如果一个参数值不能转换为一个数字将返回 NaN (非数字值)。

属性描述示例
Number.MAX_VALUE表示 JavaScript 中可表示的最大数字值。超出此值的数字将被认为是 Infinityconsole.log(Number.MAX_VALUE); // 输出: 1.7976931348623157e+308
Number.MIN_VALUE表示 JavaScript 中可表示的最小正数字值。小于此值的数字将被认为是 0console.log(Number.MIN_VALUE); // 输出: 5e-324
Number.NaN表示 "Not-a-Number" 的特殊值,表示一个非数字值。console.log(Number.NaN); // 输出: NaN
Number.NEGATIVE_INFINITY表示负无穷大。console.log(Number.NEGATIVE_INFINITY); // 输出: -Infinity
Number.POSITIVE_INFINITY表示正无穷大。console.log(Number.POSITIVE_INFINITY); // 输出: Infinity
Number.EPSILON表示两个可表示的数字之间的最小差值。console.log(Number.EPSILON); // 输出: 2.220446049250313e-16
Number.prototypeNumber 对象的原型,所有 Number 实例都继承自这个对象。console.log(Number.prototype); // 输出: [Number.prototype]

Number 对象的实例方法

方法描述示例
toString(radix?: number): string返回表示该数字的字符串。可以指定基数(radix),用于转换成二进制、八进制、十进制或十六进制。let num: number = 255; console.log(num.toString()); // 输出: "255"
console.log(num.toString(16)); // 输出: "ff"
toFixed(digits?: number): string格式化数字为指定小数位数的字符串。let num: number = 123.456; console.log(num.toFixed(2)); // 输出: "123.46"
toExponential(precision?: number): string以指数形式返回数字的字符串表示。let num: number = 123456; console.log(num.toExponential(2)); // 输出: "1.23e+5"
toPrecision(precision?: number): string返回数字的字符串表示,其总长度为指定的精度。let num: number = 123.456; console.log(num.toPrecision(5)); // 输出: "123.46"
valueOf(): number返回 Number 对象的原始数值。let num: number = new Number(123); console.log(num.valueOf()); // 输出: 123

Number 对象的静态方法

方法描述示例
Number.isNaN(value: any): boolean判断一个值是否是 NaNconsole.log(Number.isNaN(NaN)); // 输出: true
console.log(Number.isNaN(123)); // 输出: false
Number.isFinite(value: any): boolean判断一个值是否是有限的数字。console.log(Number.isFinite(123)); // 输出: true
console.log(Number.isFinite(Infinity)); // 输出: false
Number.isInteger(value: any): boolean判断一个值是否是整数。console.log(Number.isInteger(123)); // 输出: true
console.log(Number.isInteger(123.456)); // 输出: false
Number.parseFloat(string: string): number将字符串解析为浮点数。console.log(Number.parseFloat("3.14")); // 输出: 3.14
Number.parseInt(string: string, radix?: number): number将字符串解析为整数。可以指定基数。console.log(Number.parseInt("255")); // 输出: 255
console.log(Number.parseInt("ff", 16)); // 输出: 255

TypeScript String(字符串)

String 对象用于处理文本(字符串)。

语法

var txt = new String("string");
或者更简单方式:
var txt = "string";

String 对象属性

序号属性 & 描述实例
1.constructor

对创建该对象的函数的引用。

var str = new String( "This is string" ); 
console.log("str.constructor is:" + str.constructor)

输出结果:

str.constructor is:function String() { [native code] }
2.length

返回字符串的长度。

var uname = new String("Hello World") 
console.log("Length "+uname.length)  // 输出 11
3.prototype

允许您向对象添加属性和方法。

function employee(id:number,name:string) { 
    this.id = id 
    this.name = name 
 } 
 var emp = new employee(123,"admin") 
 employee.prototype.email="admin@runoob.com" // 添加属性 email
 console.log("员工号: "+emp.id) 
 console.log("员工姓名: "+emp.name) 
 console.log("员工邮箱: "+emp.email)

String 方法

序号

方法 & 描述

实例

1.

charAt()

返回在指定位置的字符。

var str = new String("xuelang");

console.log("str.charAt(0) 为:" + str.charAt(0)); // R

console.log("str.charAt(1) 为:" + str.charAt(1)); // U

console.log("str.charAt(2) 为:" + str.charAt(2)); // N

console.log("str.charAt(3) 为:" + str.charAt(3)); // O

console.log("str.charAt(4) 为:" + str.charAt(4)); // O

console.log("str.charAt(5) 为:" + str.charAt(5)); // B

2.

charCodeAt()

返回在指定的位置的字符的 Unicode 编码。

var str = new String("xuelang");

console.log("str.charCodeAt(0) 为:" + str.charCodeAt(0)); // 82

console.log("str.charCodeAt(1) 为:" + str.charCodeAt(1)); // 85

console.log("str.charCodeAt(2) 为:" + str.charCodeAt(2)); // 78

console.log("str.charCodeAt(3) 为:" + str.charCodeAt(3)); // 79

console.log("str.charCodeAt(4) 为:" + str.charCodeAt(4)); // 79

console.log("str.charCodeAt(5) 为:" + str.charCodeAt(5)); // 66

3.

concat()

连接两个或更多字符串,并返回新的字符串。

var str1 = new String( "xuelang" );

var str2 = new String( "GOOGLE" );

var str3 = str1.concat( str2 );

console.log("str1 + str2 : "+str3) // xuelangGOOGLE

4.

indexOf()

返回某个指定的字符串值在字符串中首次出现的位置。

var str1 = new String( "xuelang" );

var index = str1.indexOf( "OO" );

console.log("查找的字符串位置 :" + index );  // 3

5.

lastIndexOf()

从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。

var str1 = new String( "This is string one and again string" );

var index = str1.lastIndexOf( "string" );

console.log("lastIndexOf 查找到的最后字符串位置 :" + index ); // 29

    

index = str1.lastIndexOf( "one" );

console.log("lastIndexOf 查找到的最后字符串位置 :" + index ); // 15

6.

localeCompare()

用本地特定的顺序来比较两个字符串。

var str1 = new String( "This is beautiful string" );

  

var index = str1.localeCompare( "This is beautiful string");  

console.log("localeCompare first :" + index );  // 0

7.

match()

查找找到一个或多个正则表达式的匹配。

var str="The rain in SPAIN stays mainly in the plain";

var n=str.match(/ain/g);  // ain,ain,ain

8.

replace()

替换与正则表达式匹配的子串

var re = /(\w+)\s(\w+)/;

var str = "zara ali";

var newstr = str.replace(re, "$2, $1");

console.log(newstr); // ali, zara

9.

search()

检索与正则表达式相匹配的值

var re = /apples/gi;

var str = "Apples are round, and apples are juicy.";

if (str.search(re) == -1 ) {

   console.log("Does not contain Apples" );

} else {

   console.log("Contains Apples" );

}

10.

slice()

提取字符串的片断,并在新的字符串中返回被提取的部分。

11.

split()

把字符串分割为子字符串数组。

var str = "Apples are round, and apples are juicy.";

var splitted = str.split(" ", 3);

console.log(splitted)  // [ 'Apples', 'are', 'round,' ]

12.

substr()

从起始索引号提取字符串中指定数目的字符。

13.

substring()

提取字符串中两个指定的索引号之间的字符。

var str = "xuelang GOOGLE TAOBAO FACEBOOK";

console.log("(1,2): "    + str.substring(1,2));   // U

console.log("(0,10): "   + str.substring(0, 10)); // xuelang GOO

console.log("(5): "      + str.substring(5));     // B GOOGLE TAOBAO FACEBOOK

14.

toLocaleLowerCase()

根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射。

var str = "xuelang Google";

console.log(str.toLocaleLowerCase( ));  // xuelang google

15.

toLocaleUpperCase()

据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射。

var str = "xuelang Google";

console.log(str.toLocaleUpperCase( ));  // xuelang GOOGLE

16.

toLowerCase()

把字符串转换为小写。

var str = "xuelang Google";

console.log(str.toLowerCase( ));  // xuelang google

17.

toString()

返回字符串。

var str = "xuelang";

console.log(str.toString( )); // xuelang

18.

toUpperCase()

把字符串转换为大写。

var str = "xuelang Google";

console.log(str.toUpperCase( ));  // xuelang GOOGLE

19.

valueOf()

返回指定字符串对象的原始值。

var str = new String("xuelang");

console.log(str.valueOf( ));  // xuelang

TypeScript Array(数组)

数组对象是使用单独的变量名来存储一系列的值。

数组非常常用。

假如你有一组数据(例如:网站名字),存在单独变量如下所示:

var site1="Google";
var site2="xuelang";
var site3="Taobao";

如果有 10 个、100 个这种方式就变的很不实用,这时我们可以使用数组来解决:

var sites:string[]; 
sites = ["Google","xuelang","Taobao"]

TypeScript 声明数组的语法格式如下所示:

var array_name[:datatype];        //声明 
array_name = [val1,val2,valn..]   //初始化

或者直接在声明时初始化:

var array_name[:datatype] = [val1,val2…valn]

如果数组声明时未设置类型,则会被认为是 any 类型,在初始化时根据第一个元素的类型来推断数组的类型。

实例

创建一个 number 类型的数组:

var numlist:number[] = [2,4,6,8]

整个数组结构如下所示:

索引值第一个为 0,我们可以根据索引值来访问数组元素:

var sites:string[]; 
sites = ["Google","xuelang","Taobao"] 
console.log(sites[0]); 
console.log(sites[1]);

以下实例我们在声明时直接初始化:

var nums:number[] = [1,2,3,4] 
console.log(nums[0]); 
console.log(nums[1]); 
console.log(nums[2]); 
console.log(nums[3]);

Array 对象

属性描述示例
Array.length表示数组的长度,即数组中元素的个数。let arr = [1, 2, 3]; console.log(arr.length); // 输出: 3
Array.prototypeArray 对象的原型,所有 Array 实例都继承自这个对象。console.log(Array.prototype); // 输出: [Array.prototype]

Array 对象的实例方法
方法描述示例
concat(...items: any[]): T[]连接两个或更多的数组,并返回一个新数组。let arr1 = [1, 2]; let arr2 = [3, 4]; console.log(arr1.concat(arr2)); // 输出: [1, 2, 3, 4]
copyWithin(target: number, start?: number, end?: number): this将数组的一个部分复制到数组的另一部分,并返回修改后的数组。let arr = [1, 2, 3, 4, 5]; arr.copyWithin(0, 3, 5); console.log(arr); // 输出: [4, 5, 3, 4, 5]
every(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean测试数组中的所有元素是否都通过了测试函数的测试。let arr = [1, 2, 3]; console.log(arr.every(num => num > 0)); // 输出: true
fill(value: T, start?: number, end?: number): this用静态值填充数组的部分或全部元素。let arr = [1, 2, 3]; arr.fill(0); console.log(arr); // 输出: [0, 0, 0]
filter(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[]创建一个新数组,其中包含所有通过测试函数的元素。let arr = [1, 2, 3, 4, 5]; console.log(arr.filter(num => num % 2 === 0)); // 输出: [2, 4]
`find(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): Tundefined`返回数组中满足测试函数的第一个元素。如果没有找到则返回 undefined
findIndex(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number返回数组中满足测试函数的第一个元素的索引。如果没有找到则返回 -1。let arr = [1, 2, 3, 4, 5]; console.log(arr.findIndex(num => num > 3)); // 输出: 3
forEach(callback: (value: T, index: number, array: T[]) => void, thisArg?: any): void对数组中的每个元素执行一次指定的函数。let arr = [1, 2, 3]; arr.forEach(num => console.log(num)); // 输出: 1 2 3
includes(valueToFind: T, fromIndex?: number): boolean判断数组是否包含指定的值,返回布尔值。let arr = [1, 2, 3]; console.log(arr.includes(2)); // 输出: true
indexOf(searchElement: T, fromIndex?: number): number返回数组中第一次出现指定元素的索引。如果没有找到则返回 -1。let arr = [1, 2, 3, 2]; console.log(arr.indexOf(2)); // 输出: 1
join(separator?: string): string将数组的所有元素连接成一个字符串,元素之间用指定的分隔符分隔。let arr = [1, 2, 3]; console.log(arr.join('-')); // 输出: "1-2-3"
map(callback: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]创建一个新数组,其中包含通过测试函数处理后的所有元素。let arr = [1, 2, 3]; console.log(arr.map(num => num * 2)); // 输出: [2, 4, 6]
`pop(): Tundefined`删除数组的最后一个元素,并返回该元素。
push(...items: T[]): number向数组的末尾添加一个或多个元素,并返回新数组的长度。let arr = [1, 2]; console.log(arr.push(3, 4)); // 输出: 4
reduce(callback: (accumulator: U, value: T, index: number, array: T[]) => U, initialValue?: U): U将数组中的所有元素通过函数累积成一个单一的值。let arr = [1, 2, 3]; console.log(arr.reduce((sum, num) => sum + num, 0)); // 输出: 6
reduceRight(callback: (accumulator: U, value: T, index: number, array: T[]) => U, initialValue?: U): U类似于 reduce,但从数组的最后一个元素开始进行累积。let arr = [1, 2, 3]; console.log(arr.reduceRight((sum, num) => sum + num, 0)); // 输出: 6
reverse(): T[]反转数组中元素的顺序,并返回数组。let arr = [1, 2, 3]; console.log(arr.reverse()); // 输出: [3, 2, 1]
`shift(): Tundefined`从数组中删除第一个元素,并返回该元素。
slice(start?: number, end?: number): T[]返回数组中指定开始和结束位置之间的元素的浅拷贝。let arr = [1, 2, 3, 4]; console.log(arr.slice(1, 3)); // 输出: [2, 3]
sort(compareFn?: (a: T, b: T) => number): this对数组的元素进行排序。可以指定比较函数。let arr = [3, 1, 2]; arr.sort(); console.log(arr); // 输出: [1, 2, 3]
splice(start: number, deleteCount?: number, ...items: T[]): T[]从数组中删除或添加元素,并返回被删除的元素。let arr = [1, 2, 3, 4]; console.log(arr.splice(1, 2, 5, 6)); // 输出: [2, 3]
`toLocaleString(locales?: stringstring[], options?: Intl.NumberFormatOptions): string`将数组转换为本地化字符串。
toString(): string将数组转换为字符串。let arr = [1, 2, 3]; console.log(arr.toString()); // 输出: "1,2,3"
unshift(...items: T[]): number向数组的开头添加一个或多个元素,并返回新数组的长度。let arr = [2, 3]; console.log(arr.unshift(1)); // 输出: 3

Array 对象的静态方法
方法描述示例
Array.isArray(value: any): value is T[]判断一个值是否是数组。console.log(Array.isArray([1, 2, 3])); // 输出: true
console.log(Array.isArray({})); // 输出: false
`Array.from(arrayLike: ArrayLike<any>Iterable<any>, mapFn?: (value: any, index: number) => any, thisArg?: any): T[]`从类数组对象或可迭代对象创建一个新数组实例。
Array.of(...elements: T[]): T[]创建一个新数组实例,其元素是通过参数传入的。console.log(Array.of(1, 2, 3)); // 输出: [1, 2, 3]
Array.prototype.flat(depth?: number): T[]将数组的嵌套数组展平成一维数组。可指定展平的深度。let arr = [1, [2, [3, [4]]]]; console.log(arr.flat(2)); // 输出: [1, 2, 3, [4]]
Array.prototype.flatMap(callback: (value: T, index: number, array: T[]) => U[], thisArg?: any): U[]先使用 map 方法,然后使用 flat 方法展平结果。let arr = [1, 2, 3]; console.log(arr.flatMap(x => [x, x * 2])); // 输出: [1, 2, 2, 4, 3, 6]
Array.prototype.from创建一个新的数组实例。let arr = Array.from('123'); console.log(arr); // 输出: [ '1', '2', '3' ]
Array.prototype.keys()返回一个新的 Array Iterator 对象,该对象包含数组中每个索引的键。let arr = ['a', 'b', 'c']; let iterator = arr.keys(); console.log([...iterator]); // 输出: [0, 1, 2]
Array.prototype.values()返回一个新的 Array Iterator 对象,该对象包含数组中每个元素的值。let arr = ['a', 'b', 'c']; let iterator = arr.values(); console.log([...iterator]); // 输出: ['a', 'b', 'c']
Array.prototype.entries()返回一个新的 Array Iterator 对象,该对象包含数组中每个索引和元素的键值对。let arr = ['a', 'b', 'c']; let iterator = arr.entries(); console.log([...iterator]); // 输出: [[0, 'a'], [1, 'b'], [2, 'c']]

TypeScript Map 对象

Map 对象保存键值对,并且能够记住键的原始插入顺序。

任何值(对象或者原始值) 都可以作为一个键或一个值。

Map 是 ES6 中引入的一种新的数据结构,可以参考 ES6 Map 与 Set

创建 Map

TypeScript 使用 Map 类型和 new 关键字来创建 Map:

let myMap = new Map();

初始化 Map,可以以数组的格式来传入键值对:

let myMap = new Map([
        ["key1", "value1"],
        ["key2", "value2"]
    ]); 

Map 相关的函数与属性:

  • map.clear() – 移除 Map 对象的所有键/值对 。
  • map.set() – 设置键值对,返回该 Map 对象。
  • map.get() – 返回键对应的值,如果不存在,则返回 undefined。
  • map.has() – 返回一个布尔值,用于判断 Map 中是否包含键对应的值。
  • map.delete() – 删除 Map 中的元素,删除成功返回 true,失败返回 false。
  • map.size – 返回 Map 对象键/值对的数量。
  • map.keys() - 返回一个 Iterator 对象, 包含了 Map 对象中每个元素的键 。
  • map.values() – 返回一个新的Iterator对象,包含了Map对象中每个元素的值 。
  • map.entries() – 返回一个包含 Map 中所有键值对的迭代器 。

Map 对象的实例方法

方法描述示例
clear(): void清除 Map 对象中的所有键值对。let map = new Map([['a', 1], ['b', 2]]); map.clear(); console.log(map.size); // 输出: 0
delete(key: K): boolean删除 Map 对象中指定的键及其对应的值。let map = new Map([['a', 1], ['b', 2]]); map.delete('a'); console.log(map.has('a')); // 输出: false
entries(): IterableIterator<[K, V]>返回一个新的迭代器对象,包含 Map 对象中每个键值对的数组。let map = new Map([['a', 1], ['b', 2]]); for (let [key, value] of map.entries()) { console.log(key, value); } // 输出: a 1\nb 2
forEach(callback: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): voidMap 对象中的每个键值对执行一次指定的函数。let map = new Map([['a', 1], ['b', 2]]); map.forEach((value, key) => console.log(key, value)); // 输出: a 1\nb 2
`get(key: K): Vundefined`返回 Map 对象中指定键的值。如果键不存在则返回 undefined
has(key: K): boolean判断 Map 对象是否包含指定的键。let map = new Map([['a', 1], ['b', 2]]); console.log(map.has('b')); // 输出: true
keys(): IterableIterator<K>返回一个新的迭代器对象,包含 Map 对象中每个键的集合。let map = new Map([['a', 1], ['b', 2]]); for (let key of map.keys()) { console.log(key); } // 输出: a\nb
set(key: K, value: V): this设置 Map 对象中指定键的值。如果键已存在,则更新其值。let map = new Map(); map.set('a', 1).set('b', 2); console.log(map.get('b')); // 输出: 2
size: number返回 Map 对象中键值对的数量。let map = new Map([['a', 1], ['b', 2]]); console.log(map.size); // 输出: 2
values(): IterableIterator<V>返回一个新的迭代器对象,包含 Map 对象中每个值的集合。let map = new Map([['a', 1], ['b', 2]]); for (let value of map.values()) { console.log(value); } // 输出: 1\n2

Map 对象的静态方法

方法描述示例
Map.of(...entries: [K, V][]): Map<K, V>创建一个新的 Map 对象,其元素由参数提供的键值对组成。let map = Map.of(['a', 1], ['b', 2]); console.log(map.get('b')); // 输出: 2
`Map.prototype.from(arrayLike: ArrayLike<[K, V]>Iterable<[K, V]>, mapFn?: (value: [K, V], index: number) => [K, V], thisArg?: any): Map<K, V>`从类数组对象或可迭代对象创建一个新 Map 实例。

TypeScript 元组

元组的定义和基本用法

定义元组

元组通过指定元素类型的数组类型来定义。每个元素的类型和位置都可以被定义。

let tuple: [string, number, boolean];
tuple = ['hello', 42, true]; // 正确
// tuple = [42, 'hello', true]; // 错误:类型不匹配
访问元组元素

可以使用索引访问元组中的元素,索引从 0 开始。

let tuple: [string, number, boolean] = ['hello', 42, true];
console.log(tuple[0]); // 输出: "hello"
console.log(tuple[1]); // 输出: 42
console.log(tuple[2]); // 输出: true
修改元组元素

可以修改元组中的元素,但必须符合定义的类型。

let tuple: [string, number, boolean] = ['hello', 42, true];
tuple[1] = 100; // 正确
// tuple[1] = 'world'; // 错误:类型不匹配

元组的特性和高级用法

可选元素

可以在元组中定义可选的元素。

let tuple: [string, number?, boolean?] = ['hello', 42];
tuple = ['hello', 42, true]; // 正确
tuple = ['hello']; // 也是正确,因为后面的元素是可选的
元组解构

可以通过解构赋值来提取元组中的值。

let tuple: [string, number, boolean] = ['hello', 42, true];
let [a, b, c] = tuple;
console.log(a); // 输出: "hello"
console.log(b); // 输出: 42
console.log(c); // 输出: true
剩余元组元素

可以使用剩余元素(Rest Elements)来处理不确定数量的元素。

let tuple: [string, ...number[]] = ['hello', 1, 2, 3];
console.log(tuple); // 输出: ["hello", 1, 2, 3]

元组与其他类型的结合

元组与数组的结合

元组可以与普通数组结合使用,作为函数的参数或返回值类型。

function processTuple(...args: [string, number, boolean]): void {
    console.log(args[0]); // string
    console.log(args[1]); // number
    console.log(args[2]); // boolean
}

processTuple('test', 123, true);
元组作为对象类型的字段

元组可以用作对象的字段类型。

interface MyObject {
    name: string;
    data: [number, boolean];
}

let obj: MyObject = {
    name: 'Example',
    data: [42, true]
};

元组的常见操作

合并元组

可以使用类型合并来定义新的元组类型。

type TupleA = [string, number];
type TupleB = [boolean, ...number[]];
type CombinedTuple = TupleA | TupleB;

let example1: CombinedTuple = ['hello', 42];
let example2: CombinedTuple = [true, 1, 2, 3];
元组类型的推断

TypeScript 会根据元组的初始值推断出元组的类型。

let tuple = ['TypeScript', 2024] as const; // 元组类型推断为 [ "TypeScript", 2024 ]
// tuple[0] = 'JavaScript'; // 错误,因为元组是只读的

TypeScript 联合类型

联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。

注意:只能赋值指定的类型,如果赋值其它类型就会报错。

创建联合类型的语法格式如下:

Type1|Type2|Type3 

实例

声明联合类型:

var val:string|number 
val = 12 
console.log("数字为 "+ val) 
val = "xuelang" 
console.log("字符串为 " + val)

输出结果为

数字为 12
字符串为 xuelang

如果赋值其它类型就会报错:

var val:string|number 
val = true 

也可以将联合类型作为函数参数使用:

function disp(name:string|string[]) { 
        if(typeof name == "string") { 
                console.log(name) 
        } else { 
                var i; 
                for(i = 0;i<name.length;i++) { 
                console.log(name[i])
                } 
        } 
} 
disp("Runoob") 
console.log("输出数组....") 
disp(["xuelang","Google","Taobao","Facebook"])

联合类型数组

我们也可以将数组声明为联合类型:

var arr:number[]|string[]; 
var i:number; 
arr = [1,2,4] 
console.log("**数字数组**")  
 
for(i = 0;i<arr.length;i++) { 
   console.log(arr[i]) 
}  
 
arr = ["xuelang","Google","Taobao"] 
console.log("**字符串数组**")  
 
for(i = 0;i<arr.length;i++) { 
   console.log(arr[i]) 
}

TypeScript 接口

在 TypeScript 中,接口(interface)用于定义对象的结构,包括它们的属性、方法和索引签名。接口可以帮助你定义对象的类型,并且可以用于描述函数类型、类实现和混入等。以下是接口的所有主要特性和用法的详细列表:

接口的基本特性

特性描述示例
接口定义定义对象的结构,包括属性和方法。interface Person { name: string; age: number; }
属性类型接口中的属性可以有不同的类型。interface Person { name: string; age: number; }
方法定义接口可以定义方法。interface Person { name: string; greet(): void; }
可选属性属性可以是可选的,使用 ? 表示。interface Person { name: string; age?: number; }
只读属性属性可以是只读的,使用 readonly 关键字表示。interface Person { readonly id: number; name: string; }
函数类型接口可以描述函数类型。interface Greeter { (message: string): void; }
索引签名描述对象可以使用特定类型的索引访问。interface StringDictionary { [key: string]: string; }
继承接口接口可以继承其他接口。interface Employee extends Person { employeeId: number; }
混入(Mixins)接口可以用于实现混入模式。interface Speaker { speak(): void; }
interface Worker { work(): void; }
class Employee implements Speaker, Worker { ... }
类实现接口可以用于定义类的结构。interface Person { name: string; }
class Employee implements Person { name: string; }
联合类型接口支持联合类型。`interface Response { success: true; data: string; }
交叉类型接口支持交叉类型。interface A { x: number; }
interface B { y: number; }
type C = A & B;

接口的高级特性

特性描述示例
泛型接口接口可以使用泛型来定义。interface Box<T> { content: T; }
let stringBox: Box<string> = { content: 'hello' };
只读数组接口可以定义只读数组。interface ReadOnlyArray<T> { readonly [index: number]: T; }
索引签名的泛型索引签名也可以使用泛型。interface GenericDictionary<T> { [key: string]: T; }
构造签名接口可以定义构造函数的签名。interface PersonConstructor { new(name: string): Person; }
静态成员接口不能定义静态成员,但可以用类型别名模拟。

type StaticPerson = { new(name: string): Person; };

​​​​​​​

示例

基本接口
interface Person {
    name: string;
    age: number;
}

let person: Person = {
    name: 'Alice',
    age: 30
};
可选属性
interface Person {
    name: string;
    age?: number;
}

let person: Person = {
    name: 'Bob'
};
只读属性
interface Person {
    readonly id: number;
    name: string;
}

let person: Person = {
    id: 1,
    name: 'Charlie'
};

// person.id = 2; // 错误:只读属性
方法定义
interface Person {
    name: string;
    greet(): void;
}

let person: Person = {
    name: 'Diana',
    greet() {
        console.log(`Hello, my name is ${this.name}`);
    }
};

person.greet(); // 输出: Hello, my name is Diana
函数类型
interface Greeter {
    (message: string): void;
}

let greeter: Greeter = function(message: string) {
    console.log(message);
};

greeter('Hello, world!'); // 输出: Hello, world!
索引签名
interface StringDictionary {
    [key: string]: string;
}

let dict: StringDictionary = {
    hello: 'world',
    foo: 'bar'
};
继承接口
interface Person {
    name: string;
}

interface Employee extends Person {
    employeeId: number;
}

let employee: Employee = {
    name: 'Eve',
    employeeId: 123
};
泛型接口
interface Box<T> {
    content: T;
}

let stringBox: Box<string> = { content: 'hello' };
let numberBox: Box<number> = { content: 123 };
构造签名
interface PersonConstructor {
    new(name: string): Person;
}

class Person {
    constructor(public name: string) {}
}

function createPerson(constructor: PersonConstructor, name: string): Person {
    return new constructor(name);
}

let person = createPerson(Person, 'Frank');
console.log(person.name); // 输出: Frank

TypeScript 类

在 TypeScript 中,类(class)用于创建对象的蓝图,并支持面向对象编程(OOP)中的主要概念,如继承、封装和多态。

类的基本特性

特性描述示例
类定义定义一个类,包括属性和方法。class Person { name: string; age: number; }
构造函数用于初始化类的实例。class Person { constructor(public name: string) {} }
方法定义类的行为。class Person { greet() { console.log('Hello'); } }
属性定义类的状态。class Person { name: string; age: number; }
访问修饰符控制属性和方法的可见性,分为 public, protected, privateclass Person { private age: number; }
继承允许一个类继承另一个类的属性和方法。class Employee extends Person { employeeId: number; }
方法重写子类可以重写父类的方法。class Employee extends Person { greet() { console.log('Hello from Employee'); } }
抽象类不能实例化的类,只能被继承。abstract class Animal { abstract makeSound(): void; }
接口实现类可以实现接口,强制执行特定的结构。class Dog implements Animal { makeSound() { console.log('Woof'); } }
静态属性和方法属于类本身,而不是类的实例。class MathUtil { static PI = 3.14; static calculateCircumference(radius: number) { return 2 * MathUtil.PI * radius; } }
Getter 和 Setter定义属性的访问器和设置器。class Person { private _age: number; get age() { return this._age; } set age(value: number) { this._age = value; } }
类的静态块在类中运行静态初始化代码块。class Config { static { console.log('Static block executed'); } }
类的混入(Mixins)类可以混入其他类的功能。class Person { name: string; } class Worker { work() {} } class Employee extends Person { constructor() { super(); Object.assign(this, new Worker()); } }

类的详细示例

基本类定义
class Person {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`Hello, my name is ${this.name}`);
    }
}

const person = new Person('Alice', 30);
person.greet(); // 输出: Hello, my name is Alice

构造函数
class Person {
    constructor(public name: string, public age: number) {}
}

const person = new Person('Bob', 25);
console.log(person.name); // 输出: Bob

访问修饰符
class Person {
    private age: number;

    constructor(name: string, age: number) {
        this.age = age;
    }

    public getAge(): number {
        return this.age;
    }
}

const person = new Person('Charlie', 35);
console.log(person.getAge()); // 输出: 35

继承
class Animal {
    constructor(public name: string) {}

    makeSound() {
        console.log('Some generic animal sound');
    }
}

class Dog extends Animal {
    makeSound() {
        console.log('Woof');
    }
}

const dog = new Dog('Rex');
dog.makeSound(); // 输出: Woof
抽象类
abstract class Animal {
    abstract makeSound(): void;

    move() {
        console.log('Moving...');
    }
}

class Dog extends Animal {
    makeSound() {
        console.log('Woof');
    }
}

const dog = new Dog();
dog.makeSound(); // 输出: Woof
dog.move(); // 输出: Moving...
接口实现
interface Animal {
    name: string;
    makeSound(): void;
}

class Dog implements Animal {
    constructor(public name: string) {}

    makeSound() {
        console.log('Woof');
    }
}

const dog = new Dog('Rex');
dog.makeSound(); // 输出: Woof
静态属性和方法
class MathUtil {
    static PI = 3.14;

    static calculateCircumference(radius: number) {
        return 2 * MathUtil.PI * radius;
    }
}

console.log(MathUtil.calculateCircumference(5)); // 输出: 31.400000000000002
Getter 和 Setter
class Person {
    private _age: number;

    constructor(public name: string, age: number) {
        this._age = age;
    }

    get age(): number {
        return this._age;
    }

    set age(value: number) {
        if (value > 0) {
            this._age = value;
        }
    }
}

const person = new Person('Alice', 30);
console.log(person.age); // 输出: 30
person.age = 35;
console.log(person.age); // 输出: 35
类的静态块
class Config {
    static {
        console.log('Static block executed');
    }
}
类的混入(Mixins)
class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
}

class Worker {
    work() {
        console.log('Working');
    }
}

class Employee extends Person {
    constructor(name: string) {
        super(name);
        Object.assign(this, new Worker());
    }
}

const employee = new Employee('Eve');
employee.work(); // 输出: Working

TypeScript 对象

在 TypeScript 中,对象(Object)是一种复杂的数据结构,用于存储键值对。对象类型可以用来表示类、接口和字面量形式的数据结构。TypeScript 对对象类型进行了严格的类型检查,并提供了许多与对象相关的特性和功能。

对象的基本特性

特性描述示例
对象字面量直接定义对象的属性和方法。let person = { name: 'Alice', age: 30 };
类型推断TypeScript 会根据对象字面量推断其类型。let person = { name: 'Alice', age: 30 }; // 推断为 { name: string; age: number }
可选属性对象的属性可以是可选的。let person: { name: string; age?: number } = { name: 'Alice' };
只读属性对象的属性可以是只读的,不能被修改。let person: { readonly name: string } = { name: 'Alice' };
嵌套对象对象可以包含其他对象作为属性。let company = { name: 'TechCorp', address: { city: 'New York', zip: '10001' } };
索引签名定义对象的动态属性,属性名称不固定。let dictionary: { [key: string]: string } = { hello: 'world' };
对象解构可以从对象中提取属性到变量中。let { name, age } = person;
对象类型的别名可以使用 typeinterface 定义对象类型的别名。type Person = { name: string; age: number; };
联合类型对象类型可以与其他类型结合,创建联合类型。`type Response = { success: true; data: string }
交叉类型对象类型可以与其他对象类型结合,创建交叉类型。type FullInfo = Person & ContactInfo;

对象的高级特性

特性描述示例
对象的可变性使用 readonly 修饰符可以限制对象属性的修改。const obj: { readonly id: number } = { id: 1 };
对象属性的遍历使用 for...in 循环遍历对象的属性。for (let key in person) { console.log(key, person[key]); }
对象方法对象可以包含方法(函数)作为其属性。let person = { name: 'Alice', greet() { console.log('Hello'); } };
对象与接口的结合对象类型可以与接口结合使用。interface Person { name: string; age: number; }
let person: Person = { name: 'Alice', age: 30 };
对象的扩展和混入使用 Object.assign 或其他方式扩展对象的属性。let extended = Object.assign({}, obj, { age: 30 });
对象类型的类型检查使用类型守卫(Type Guards)检查对象类型。if ('name' in person) { console.log(person.name); }
对象属性的条件类型使用 keyof 和条件类型来动态创建对象类型。`type Keys = keyof Person; // 'name'

对象的详细示例

对象字面量
let person = {
    name: 'Alice',
    age: 30
};

console.log(person.name); // 输出: Alice
console.log(person.age);  // 输出: 30
可选属性
let person: { name: string; age?: number } = {
    name: 'Bob'
};

console.log(person.name); // 输出: Bob
console.log(person.age);  // 输出: undefined
只读属性
let person: { readonly name: string } = {
    name: 'Charlie'
};

// person.name = 'Dave'; // 错误: 不能修改只读属性
嵌套对象
let company = {
    name: 'TechCorp',
    address: {
        city: 'New York',
        zip: '10001'
    }
};

console.log(company.address.city); // 输出: New York
索引签名
let dictionary: { [key: string]: string } = {
    hello: 'world',
    goodbye: 'moon'
};

console.log(dictionary['hello']); // 输出: world
对象解构
let person = { name: 'Eve', age: 40 };
let { name, age } = person;

console.log(name); // 输出: Eve
console.log(age);  // 输出: 40
对象类型的别名
type Person = {
    name: string;
    age: number;
};

let person: Person = {
    name: 'Frank',
    age: 50
};
联合类型的对象
type Person = {
    name: string;
    age: number;
};

let person: Person = {
    name: 'Frank',
    age: 50
};
联合类型的对象
type SuccessResponse = { success: true; data: string };
type ErrorResponse = { success: false; error: string };
type Response = SuccessResponse | ErrorResponse;

let response: Response = { success: true, data: 'Data loaded' };

if (response.success) {
    console.log(response.data); // 输出: Data loaded
} else {
    console.log(response.error);
}
交叉类型的对象
type Person = { name: string; age: number };
type ContactInfo = { phone: string; email: string };

type FullInfo = Person & ContactInfo;

let person: FullInfo = {
    name: 'Grace',
    age: 45,
    phone: '123-456-7890',
    email: 'grace@example.com'
};
对象的扩展与混入
let obj1 = { name: 'Henry' };
let obj2 = { age: 60 };

let person = Object.assign({}, obj1, obj2);

console.log(person); // 输出: { name: 'Henry', age: 60 }

TypeScript 泛型

泛型(Generics)是一种编程语言特性,允许在定义函数、类、接口等时使用占位符来表示类型,而不是具体的类型。

泛型是一种在编写可重用、灵活且类型安全的代码时非常有用的功能。

使用泛型的主要目的是为了处理不特定类型的数据,使得代码可以适用于多种数据类型而不失去类型检查。

泛型的优势包括:

  • 代码重用: 可以编写与特定类型无关的通用代码,提高代码的复用性。

  • 类型安全: 在编译时进行类型检查,避免在运行时出现类型错误。

  • 抽象性: 允许编写更抽象和通用的代码,适应不同的数据类型和数据结构。

泛型的主要特性

特性描述示例
泛型函数函数可以接受任意类型作为参数和返回值。function identity<T>(arg: T): T { return arg; }
泛型接口接口可以使用泛型定义,以支持不同类型的对象。interface Box<T> { value: T; }
泛型类类可以使用泛型定义,以支持不同类型的属性和方法。class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; }
泛型约束可以对泛型进行约束,要求传入的类型符合一定条件。function loggingIdentity<T extends Lengthwise>(arg: T): T {}
多个类型参数可以在泛型中使用多个类型参数。function merge<T, U>(obj1: T, obj2: U): T & U { return {...obj1, ...obj2}; }
泛型默认类型可以为泛型参数提供默认类型。function createArray<T = string>(length: number, value: T): T[] {}
泛型工具类型TypeScript 提供了一些内置的泛型工具类型,如 Partial, ReadOnly, Record 等。type Partial<T> = { [P in keyof T]?: T[P] };
泛型推断TypeScript 能够自动推断泛型参数的类型。let output = identity(5); // T 被推断为 number

泛型的详细示例

泛型函数
function identity<T>(arg: T): T {
    return arg;
}

let output1 = identity<string>("Hello"); // T 被指定为 string
let output2 = identity<number>(42);      // T 被指定为 number
泛型接口
interface Box<T> {
    value: T;
}

let stringBox: Box<string> = { value: "Hello" };
let numberBox: Box<number> = { value: 42 };
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;

    constructor(zeroValue: T, addFn: (x: T, y: T) => T) {
        this.zeroValue = zeroValue;
        this.add = addFn;
    }
}

let myNumber = new GenericNumber<number>(0, (x, y) => x + y);
console.log(myNumber.add(10, 20)); // 输出: 30
泛型约束
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length); // 现在我们可以访问 `length` 属性
    return arg;
}

loggingIdentity({ length: 10, value: "Hello" });
多个类型参数
function merge<T, U>(obj1: T, obj2: U): T & U {
    return { ...obj1, ...obj2 };
}

const mergedObj = merge({ name: "Alice" }, { age: 25 });
console.log(mergedObj); // 输出: { name: 'Alice', age: 25 }
泛型默认类型
function createArray<T = string>(length: number, value: T): T[] {
    return Array(length).fill(value);
}

let stringArray = createArray(3, "Hello"); // 默认类型为 string
let numberArray = createArray<number>(3, 42); // 明确指定类型为 number
泛型工具类型

TypeScript 提供了一些内置的泛型工具类型,用于常见的类型操作:

  • Partial<T>: 将所有属性变为可选。
  • Readonly<T>: 将所有属性变为只读。
  • Record<K, T>: 构造一个类型,其属性名为 K,属性值为 T。
  • Pick<T, K>: 从 T 中挑选一组属性 K。
  • Omit<T, K>: 从 T 中删除一组属性 K。
    interface Person {
        name: string;
        age: number;
        address: string;
    }
    
    type PartialPerson = Partial<Person>; // { name?: string; age?: number; address?: string; }
    type ReadonlyPerson = Readonly<Person>; // { readonly name: string; readonly age: number; readonly address: string; }
    type NameAndAge = Pick<Person, "name" | "age">; // { name: string; age: number; }
    type AddressOnly = Omit<Person, "name" | "age">; // { address: string; }
    type StringRecord = Record<string, Person>; // { [key: string]: Person; }
    
泛型推断 
function identity<T>(arg: T): T {
    return arg;
}

let output = identity(5); // TypeScript 自动推断 T 为 number

TypeScript 命名空间

TypeScript 的命名空间(Namespace)是一种在大型代码库中组织代码的方式,它允许将代码划分为逻辑单元,并避免全局命名空间的污染。命名空间提供了一种机制,可以将变量、函数、类等组织在一起,并通过导入和导出机制进行管理

命名空间的主要特性

特性描述示例
命名空间定义使用 namespace 关键字定义命名空间。namespace MyNamespace { export function greet() { ... } }
导出成员命名空间内的成员可以通过 export 关键字导出。export function greet() { ... }
嵌套命名空间命名空间可以嵌套,形成多层结构。namespace A { export namespace B { ... } }
导入命名空间使用 import 关键字将命名空间导入到另一个模块中。import { MyNamespace } from './myNamespace';
别名导入可以使用别名导入命名空间,简化引用。import alias = MyNamespace.SomeClass;
合并命名空间TypeScript 允许同名命名空间进行合并。namespace MyNamespace { export function greet() { ... } }
内部模块和外部模块的区别内部模块使用 namespace,外部模块使用 modulemodule "myModule" { export function greet() { ... } }
命名空间与模块的结合命名空间可以与模块结合使用,增强代码的组织性和可读性。namespace Utilities { export class Logger { ... } } export = Utilities;
与 ES6 模块的对比命名空间在一定程度上与 ES6 模块有相似之处,但用于不同场景。-

命名空间的详细示例

定义一个简单的命名空间
namespace Utils {
    export function greet(name: string) {
        return `Hello, ${name}!`;
    }
}

console.log(Utils.greet("Alice")); // 输出: Hello, Alice!
嵌套命名空间
namespace Company {
    export namespace Departments {
        export function getDepartmentName() {
            return "HR";
        }
    }
}

console.log(Company.Departments.getDepartmentName()); // 输出: HR
导入命名空间
// utils.ts
export namespace Utils {
    export function greet(name: string) {
        return `Hello, ${name}!`;
    }
}

// main.ts
import { Utils } from './utils';

console.log(Utils.greet("Alice")); // 输出: Hello, Alice!
别名导入
namespace Geometry {
    export namespace Circle {
        export const PI = 3.14;
        export function calculateArea(radius: number): number {
            return PI * radius * radius;
        }
    }
}

// 使用别名导入
import CircleMath = Geometry.Circle;

console.log(CircleMath.calculateArea(5)); // 输出: 78.5
合并命名空间
// 定义一个命名空间
namespace Validation {
    export function validateName(name: string): boolean {
        return name.length > 0;
    }
}

// 扩展同一个命名空间
namespace Validation {
    export function validateAge(age: number): boolean {
        return age > 0;
    }
}

console.log(Validation.validateName("Alice")); // 输出: true
console.log(Validation.validateAge(30));       // 输出: true
命名空间与模块的结合
namespace Utilities {
    export class Logger {
        log(message: string) {
            console.log(message);
        }
    }
}

// 将命名空间作为模块导出
export = Utilities;
命名空间与 ES6 模块的对比
  • 命名空间: 更适用于大型应用程序的内部组织,将多个相关代码块组合在一起。适合在需要更好组织和封装代码的场景下使用。
  • ES6 模块: 更适用于模块化开发和打包,在现代 TypeScript 项目中更为常用。
    // ES6 模块示例
    export function greet(name: string) {
        return `Hello, ${name}!`;
    }
    
    // 导入 ES6 模块
    import { greet } from './greet';
    
    console.log(greet("Alice")); // 输出: Hello, Alice!
    

TypeScript 模块

TypeScript 模块是一种在代码中组织和封装功能的方式,它允许开发者将代码分割成多个文件,并通过导入和导出来共享代码。模块帮助保持代码的清晰性、可维护性和可重用性,特别是在大型项目中。

模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们。类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等。

两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。

模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于 Node.js 的 CommonJS 和服务于 Web 应用的 Require.js。

此外还有有 SystemJs 和 Webpack。

模块的主要特性

特性描述示例
导出成员使用 export 关键字导出变量、函数、类或接口。export const PI = 3.14;
导入成员使用 import 关键字从另一个模块中导入导出的成员。import { PI } from './math';
默认导出一个模块可以有一个默认导出,使用 export default 定义。export default function greet() { ... }
默认导入使用 import name from 语法导入默认导出。import greet from './greet';
重命名导入导出使用 as 关键字为导入或导出重命名。import { PI as CirclePI } from './math';
全部导入使用 import * as 语法将模块中的所有导出成员导入为一个对象。import * as MathUtils from './math';
动态导入使用 import() 函数动态加载模块,返回一个 Promiseconst math = await import('./math');
模块分割可以将代码分割成多个模块,按需加载,优化应用的性能。-
命名空间与模块的区别模块用于外部模块化开发,而命名空间用于内部代码组织。-
编译选项TypeScript 提供了 moduletarget 编译选项,用于指定模块的类型和目标。tsconfig.json 中设置 "module": "ESNext"

模块的详细示例 

基本导出和导入
math.ts
export const PI = 3.14;

export function calculateCircumference(diameter: number): number {
    return diameter * PI;
}
app.ts
import { PI, calculateCircumference } from './math';

console.log(`Circumference: ${calculateCircumference(10)}`); // 输出: Circumference: 31.4
默认导出和导入
greet.ts
export default function greet(name: string): string {
    return `Hello, ${name}!`;
}
app.ts
import greet from './greet';

console.log(greet("Alice")); // 输出: Hello, Alice!
重命名导入和导出 
math.ts
export const PI = 3.14;
app.ts
import { PI as CirclePI } from './math';

console.log(CirclePI); // 输出: 3.14
全部导入
math.ts
export const PI = 3.14;

export function calculateCircumference(diameter: number): number {
    return diameter * PI;
}
app.ts
import * as MathUtils from './math';

console.log(MathUtils.calculateCircumference(10)); // 输出: 31.4
动态导入
// 可以在运行时动态导入模块
async function loadMathModule() {
    const math = await import('./math');
    console.log(math.calculateCircumference(10)); // 输出: 31.4
}

loadMathModule();
使用 TypeScript 配置模块

TypeScript 提供了多个编译选项来指定模块的类型:

tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext",    // 指定使用 ES 模块
    "target": "ES6"        // 编译成 ES6 代码
  }
}
模块分割与按需加载

模块分割是一种优化技术,它可以将应用程序分割为多个模块,并根据需要动态加载这些模块。这在构建大型应用程序时尤其有用,可以减少初始加载时间。

// 使用 Webpack 或其他打包工具支持模块分割
async function loadAndUseModule() {
    const module = await import('./largeModule');
    module.doSomething();
}

TypeScript 声明文件

TypeScript 声明文件(Declaration Files)用于为 JavaScript 库或代码提供类型描述,让 TypeScript 编译器能够理解这些库或代码的类型信息。声明文件通常以 .d.ts 结尾,包含类型声明而没有实际的实现代码。

声明文件的主要特性

特性描述示例
声明文件的基本结构声明文件的基础结构类似于 TypeScript 文件,但没有具体实现。declare const myVar: string;
全局声明为全局变量或类型创建声明。declare var jQuery: (selector: string) => any;
模块声明为模块或包创建类型声明,使用 declare moduledeclare module "myModule" { export function myFunc(): void; }
命名空间声明为命名空间创建类型声明,使用 declare namespacedeclare namespace MyLib { function greet(): void; }
接口声明为对象类型定义接口。interface Person { name: string; age: number; }
函数声明为函数定义类型签名。declare function greet(name: string): void;
类声明为类创建声明,包括其方法和属性。declare class MyClass { constructor(value: string); }
枚举声明为枚举类型创建声明。declare enum Colors { Red, Green, Blue }
类型别名声明使用 type 关键字创建类型别名声明。`type StringOrNumber = string
外部库声明文件为第三方 JavaScript 库创建声明文件。declare module "lodash" { export function cloneDeep<T>(value: T): T; }
联合声明通过 declare module 声明并联合全局类型声明。declare module "jquery" { export = jQuery; }
通过 DefinitelyTyped 获取声明文件使用 @types 获取常用库的声明文件。npm install @types/jquery --save-dev

声明文件的详细示例

基本声明文件

.d.ts 文件中,你可以声明变量、函数、类、接口等,使它们在 TypeScript 项目中可用。

// globals.d.ts
declare const myGlobalVar: string;
declare function myGlobalFunction(name: string): void;
declare class MyClass {
    constructor(value: string);
    myMethod(): string;
}

使用这些声明:

// 使用声明的全局变量和函数
console.log(myGlobalVar);
myGlobalFunction("TypeScript");

// 使用声明的类
const obj = new MyClass("Hello");
console.log(obj.myMethod());
模块声明

为一个模块或包创建类型声明。适用于你想为特定的模块或包提供类型信息的情况。

// myModule.d.ts
declare module "myModule" {
    export function myFunc(): void;
}

使用模块声明:

import { myFunc } from "myModule";

myFunc();
命名空间声明

如果你正在使用一个以命名空间组织的库,可以为它创建命名空间声明。

// myLib.d.ts
declare namespace MyLib {
    function greet(name: string): void;
    const version: string;
}

使用命名空间声明:

MyLib.greet("TypeScript");
console.log(MyLib.version);
外部库声明文件

为外部 JavaScript 库创建声明文件,让 TypeScript 项目能够理解这些库的类型。

// lodash.d.ts (示例)
declare module "lodash" {
    export function cloneDeep<T>(value: T): T;
}

使用外部库声明:

import { cloneDeep } from "lodash";

const obj = { a: 1 };
const copy = cloneDeep(obj);
console.log(copy);
DefinitelyTyped 和 @types

TypeScript 社区通过 DefinitelyTyped 项目维护了大量常用库的声明文件。你可以通过 @types 包直接安装这些声明文件。

npm install @types/jquery --save-dev

安装后,你可以在 TypeScript 项目中直接使用这些库:

import * as $ from "jquery";

$(document).ready(() => {
    console.log("Document is ready!");
});

如何创建自定义声明文件

1.确定你需要声明的内容:全局变量、模块、类、函数等。
2.使用 declare 关键字:在 .d.ts 文件中使用 declare 关键字来定义类型。
3.将声明文件包含在 TypeScript 项目中:通常,声明文件应与项目的其他 TypeScript 文件一起被编译器自动检测到。如果需要,你可以在 tsconfig.json 中使用 include 或 files 选项显式包含它们。


鸣谢

感谢菜鸟教程提供本文主要的行文思路和大量的资料、示例、图片等

感谢Wikipedia提供的专业知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuelang233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值