Cocos小游戏开发-TypeScript

Cocos小游戏开发-TypeScript

参考:

  1. 使用 TypeScript 脚本
  2. TypeScript 教程

TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个严格超集,并添加了可选的静态类型和基于类的面向对象编程。TypeScript 的设计目标是开发大型应用,然后转译成 JavaScript 运行。由于 TypeScript 是 JavaScript 的超集,任何现有的 JavaScript 程序都是合法的 TypeScript 程序。

语言特性

TypeScript 是一种给 JavaScript 添加特性的语言扩展。增加的功能包括:

  • 类型批注和编译时类型检查
  • 类型推断
  • 类型擦除
  • 接口
  • 枚举
  • Mixin
  • 泛型编程
  • 名字空间
  • 元组
  • Await

以下功能是从 ECMA 2015 反向移植而来:

  • 模块
  • lambda 函数的箭头语法
  • 可选参数以及默认参数

JavaScript 与 TypeScript 的区别

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

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

TypeScript 安装

1
npm install -g typescript

安装完成后我们可以使用 tsc 命令来执行 TypeScript 的相关代码,以下是查看版本号:

1
2
$ tsc -v
Version 3.2.2

然后我们新建一个 app.ts 的文件,代码如下:

1
2
var message:string = "Hello World" 
console.log(message)

通常我们使用 .ts 作为 TypeScript 代码文件的扩展名。

然后执行以下命令将 TypeScript 转换为 JavaScript 代码:

1
tsc app.ts

这时候在当前目录下(与 app.ts 同一目录)就会生成一个 app.js 文件,代码如下:

1
2
var message = "Hello World";
console.log(message);

使用 node 命令来执行 app.js 文件:

1
2
$ node app.js 
Hello World

TypeScript 转换为 JavaScript 过程如下图:

我们可以同时编译多个 ts 文件:

1
tsc file1.ts file2.ts file3.ts

sc 常用编译参数如下表所示:

编译参数说明
–help 显示帮助信息
–module 载入扩展模块
–target 设置 ECMA 版本
–declaration 额外生成一个 .d.ts 扩展名的文件
–removeComments 删除文件的注释
–out 编译多个文件并合并到一个输出的文件
–sourcemap 生成一个 sourcemap (.map) 文件。sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。
–module noImplicitAny 在表达式和声明上有隐含的 any 类型时报错
–watch 在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。

TypeScript 语法

TypeScript 基础语法

TypeScript 程序由以下几个部分组成:

  • 模块
  • 函数
  • 变量
  • 语句和表达式
  • 注释

TypeScript 保留关键字

break as catch switch
case if throw else
var number string get
module type instanceof typeof
public private enum export
finally for while void
null super this new
in return true false
any extends static let
package implements interface function
do try yield const
continue

空白和换行

TypeScript 会忽略程序中出现的空格、制表符和换行符。

空格、制表符通常用来缩进代码,使代码易于阅读和理解。

TypeScript 区分大小写

TypeScript 区分大写和小写字符。

分号是可选的

每行指令都是一段语句,你可以使用分号或不使用, 分号在 TypeScript 中是可选的,建议使用。

以下代码都是合法的:

1
2
console.log("Runoob")
console.log("Google");

如果语句写在同一行则一定需要使用分号来分隔,否则会报错,如:

1
console.log("Runoob");console.log("Google");

TypeScript 注释

注释是一个良好的习惯,虽然很多程序员讨厌注释,但还是建议你在每段代码写上文字说明。

注释可以提高程序的可读性。

注释可以包含有关程序一些信息,如代码的作者,有关函数的说明等。

编译器会忽略注释。

TypeScript 支持两种类型的注释

  • 单行注释 ( // ) − 在 // 后面的文字都是注释内容。
  • 多行注释 (/* */) − 这种注释可以跨越多行。

TypeScript 与面向对象

面向对象是一种对现实世界理解和抽象的方法。

TypeScript 是一种面向对象的编程语言。

面向对象主要有两个概念:对象和类。

  • 对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
  • 类:类是一个模板,它描述一类对象的行为和状态。
  • 方法:方法是类的操作的实现步骤。

下图中 girl、boy 为类,而具体的每个人为该类的对象:

TypeScript 面向对象编程实例:

1
2
3
4
5
6
7
class Site { 
name():void {
console.log("Runoob")
}
}
var obj = new Site();
obj.name();

以上实例定义了一个类 Site,该类有一个方法 name(),该方法在终端上输出字符串 Runoob。

new 关键字创建类的对象,该对象调用方法 name()。

编译后生成的 JavaScript 代码如下:

1
2
3
4
5
6
7
8
9
10
var Site = /** @class */ (function () {
function Site() {
}
Site.prototype.name = function () {
console.log("Runoob");
};
return Site;
}());
var obj = new Site();
obj.name();

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

1
Runoob

TypeScript 基础类型

TypeScript 包含的数据类型如下表:

数据类型 关键字 描述
任意类型 any 声明为 any 的变量可以赋予任意类型的值。
数字类型

number

双精度 64 位浮点值。它可以用来表示整数和分数。
1
2
3
4
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744; // 八进制
let decLiteral: number = 6; // 十进制
let hexLiteral: number = 0xf00d; // 十六进制
字符串类型

string

一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式。
1
2
3
let name: string = "Runoob";
let years: number = 5;
let words: string = `您好,今年是 ${ name } 发布 ${ years + 1} 周年`;
布尔类型

boolean

表示逻辑值:true 和 false。
1
let flag: boolean = true;
数组类型

声明变量为数组。
1
2
3
4
5
// 在元素类型后面加上[]
let arr: number[] = [1, 2];

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

元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
1
2
3
4
let x: [string, number];
x = ['Runoob', 1]; // 运行正常
x = [1, 'Runoob']; // 报错
console.log(x[0]); // 输出 Runoob
枚举

enum

枚举类型用于定义数值集合。
1
2
3
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c); // 输出 2
void

void

用于标识方法返回值的类型,表示该方法没有返回值。

1
2
3
function hello(): void {
alert("Hello Runoob");
}
undefined undefined 用于初始化变量为一个未定义的值
never never never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值

Any 类型

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

  1. 变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查,示例代码如下:
    1
    2
    3
    let x: any = 1;    // 数字类型
    x = 'I am who I am'; // 字符串类型
    x = false; // 布尔类型
  2. 改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查,示例代码如下:
    1
    2
    3
    let x: any = 4;
    x.ifItExists(); // 正确,ifItExists方法在运行时可能存在,但这里并不会检查
    x.toFixed(); // 正确
  3. 定义存储各种类型数据的数组时,示例代码如下:
    1
    2
    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 或本身对应的类型,示例代码如下:

1
2
3
4
5
// 启用 --strictNullChecks
let x: number;
x = 1; // 编译正确
x = undefined; // 编译错误
x = null; // 编译错误

上面的例子中变量 x 只能是数字类型。如果一个类型可能出现 null 或 undefined, 可以用 | 来支持多种类型,示例代码如下:

1
2
3
4
5
// 启用 --strictNullChecks
let x: number | null | undefined;
x = 1; // 编译正确
x = undefined; // 编译正确
x = null; // 编译正确

never 类型

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 变量声明

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

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

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

1
2
var [变量名] : [类型] = 值;
var uname:string = "Runoob";

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

1
2
var [变量名] : [类型];
var uname:string;

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

1
2
var [变量名] = 值;
var uname = "Runoob";

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

1
2
var [变量名];
var uname;

TypeScript 遵循强类型,如果将不同的类型赋值给变量会编译错误,如下实例:

1
var num:number = "hello"     // 这个代码会编译错误

类型推断

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

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

1
2
3
4
var num = 2;    // 类型推断为 number
console.log("num 变量的值为 "+num);
num = "12"; // 编译错误
console.log(num);
  • 第一行代码声明了变量 num 并=设置初始值为 2。 注意变量声明没有指定类型。因此,程序使用类型推断来确定变量的数据类型,第一次赋值为 2,num 设置为 number 类型。
  • 第三行代码,当我们再次为变量设置字符串类型的值时,这时编译会错误。因为变量已经设置为了 number 类型
1
error TS2322: Type '"12"' is not assignable to type 'number'.

变量作用域

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

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

TypeScript 有以下几种作用域:

  • 全局作用域 − 全局变量定义在程序结构的外部,它可以在你代码的任何位置使用。
  • 类作用域 − 这个变量也可以称为 字段。类变量声明在一个类里头,但在类的方法外面。 该变量可以通过类的对象来访问。类变量也可以是静态的,静态的变量可以通过类名直接访问。
  • 局部作用域 − 局部变量,局部变量只能在声明它的一个代码块(如:方法)中使用。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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 代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 代码,输出结果为:

1
2
3
全局变量为: 12
10
实例变量: 13

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

1
error TS2322: Could not find symbol 'local_num'.

TypeScript 运算符

算术运算符

运算符 描述 例子
+ 加法 x=y+2
- 减法 x=y-2
* 乘法 x=y*2
/ 除法 x=y/2
% 取模(余数) x=y%2
++ 自增 x=++y(x=y+1)
++ 自增 x=y++(x=y; y=y+1)
自减 x=y–; x=–y

关系运算符

关系运算符用于计算结果是否为 true 或者 false。

运算符 描述
== 等于
!= 不等于
> 大于
< 小于
>= 大于或等于
<= 小于或等于

逻辑运算符

逻辑运算符用于测定变量或值之间的逻辑。

给定 x=6 以及 y=3,下表解释了逻辑运算符:

运算符 描述 例子
&& and (x < 10 && y > 1) 为 true
|| or (x==5
! not !(x==y) 为 true

三元运算符 (?)

1
Test ? expr1 : expr2
1
2
3
var num:number = -2 
var result = num > 0 ? "大于 0" : "小于 0,或等于 0"
console.log(result)

类型运算符

typeof 运算符

typeof 是一元运算符,返回操作数的数据类型。

1
2
var num = 12 
console.log(typeof num); //输出结果: number

使用 tsc 命令编译以上代码得到如下 JavaScript 代码:

1
2
var num = 12;
console.log(typeof num); //输出结果: number

instanceof

instanceof 运算符用于判断对象是否为指定的类型

字符串运算符: 连接运算符 (+)

  • 运算符可以拼接两个字符串,查看以下实例:
    1
    2
    var msg:string = "RUNOOB"+".COM" 
    console.log(msg) // RUNOOB.COM

TypeScript 条件语句

if 语句

1
2
3
4
5
var  num:number = 5
if (num > 0) {
console.log("数字是正数")
}

if…else 语句

1
2
3
4
5
6
var num:number = 12; 
if (num % 2==0) {
console.log("偶数");
} else {
console.log("奇数");
}

if…else if….else 语句

1
2
3
4
5
6
7
8
var num:number = 2 
if(num > 0) {
console.log(num+" 是正数")
} else if(num < 0) {
console.log(num+" 是负数")
} else {
console.log(num+" 不是正数也不是负数")
}

switch…case 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var grade:string = "A"; 
switch(grade) {
case "A": {
console.log("优");
break;
}
case "B": {
console.log("良");
break;
}
case "C": {
console.log("及格");
break;
}
case "D": {
console.log("不及格");
break;
}
default: {
console.log("非法输入");
break;
}
}

TypeScript 循环

for 循环

1
2
3
4
5
6
7
8
var num:number = 5; 
var i:number;
var factorial = 1;

for(i = num;i>=1;i--) {
factorial *= i;
}
console.log(factorial)

for…in 循环

1
2
3
4
5
6
var j:any; 
var n:any = "a b c"

for(j in n) {
console.log(n[j])
}

输出

1
2
3
4
5
a

b

c

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(集合)等可迭代的数据结构等。

1
2
3
4
5
let someArray = [1, "string", false];

for (let entry of someArray) {
console.log(entry); // 1, "string", false
}

forEach、every 和 some 是 JavaScript 的循环语法,TypeScript 作为 JavaScript 的语法超集,当然默认也是支持的。

因为 forEach 在 iteration 中是无法返回的,所以可以使用 every 和 some 来取代 forEach。

1
2
3
4
5
6
let list = [4, 5, 6];
list.forEach((val, idx, array) => {
// val: 当前值
// idx:当前index
// array: Array
});
1
2
3
4
5
6
7
8
let list = [4, 5, 6];
list.every((val, idx, array) => {
// val: 当前值
// idx:当前index
// array: Array
return true; // Continues
// Return false will quit the iteration
});

while 循环

1
2
3
4
5
6
7
8
var num:number = 5; 
var factorial:number = 1;

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

do…while 循环

不像 for 和 while 循环,它们是在循环头部测试循环条件。do…while 循环是在循环的尾部检查它的条件。

1
2
3
4
5
var n:number = 10;
do {
console.log(n);
n--;
} while(n>=0);

无限循环

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

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

1
2
3
for(;;) { 
console.log("这段代码会不停的执行")
}

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

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

TypeScript 函数

1
2
3
4
function function_name()
{
// 执行代码
}

调用函数

1
function_name()

函数返回值

1
2
3
4
function function_name():return_type { 
// 语句
return value;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
// 函数定义
function greet():string { // 返回一个字符串
return "Hello World"
}

function caller() {
var msg = greet() // 调用 greet() 函数
console.log(msg)
}

// 调用函数
caller()

带参数函数

1
2
3
4
function add(x: number, y: number): number {
return x + y;
}
console.log(add(1,2))

可选参数和默认参数

在 TypeScript 函数里,如果我们定义了参数,则我们必须传入这些参数,除非将这些参数设置为可选,可选参数使用问号标识 ?。

实例

1
2
3
4
5
6
7
8
9
10
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}

let result1 = buildName("Bob"); // 正确
let result2 = buildName("Bob", "Adams", "Sr."); // 错误,参数太多了
let result3 = buildName("Bob", "Adams"); // 正确

可选参数必须跟在必需参数后面。 如果上例我们想让 firstName 是可选的,lastName 必选,那么就要调整它们的位置,把 firstName 放在后面。

如果都是可选参数就没关系。

默认参数

参数不能同时设置为可选和默认。

1
2
3
4
5
6
function calculate_discount(price:number,rate:number = 0.50) { 
var discount = price * rate;
console.log("计算结果: ",discount);
}
calculate_discount(1000)
calculate_discount(1000,0.30)

剩余参数

有一种情况,我们不知道要向函数传入多少个参数,这时候我们就可以使用剩余参数来定义。

剩余参数语法允许我们将一个不确定数量的参数作为一个数组传入。

1
2
3
4
5
6
7
8
9
10
11
function addNumbers(...nums:number[]) {  
var i;
var sum:number = 0;

for(i = 0;i<nums.length;i++) {
sum = sum + nums[i];
}
console.log("和为:",sum)
}
addNumbers(1,2,3)
addNumbers(10,10,10,10,10)

匿名函数

匿名函数是一个没有函数名的函数。

匿名函数在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样。

我们可以将匿名函数赋值给一个变量,这种表达式就成为函数表达式。

1
2
3
4
var msg = function() { 
return "hello world";
}
console.log(msg()) // hello world

带参数匿名函数:

1
2
3
4
var res = function(a:number,b:number) { 
return a*b;
};
console.log(res(12,2))

匿名函数自调用

匿名函数自调用在函数后使用 () 即可:

1
2
3
4
(function () { 
var x = "Hello!!";
console.log(x)
})()

构造函数

TypeScript 也支持使用 JavaScript 内置的构造函数 Function() 来定义函数:
语法格式如下:

1
var res = new Function ([arg1[, arg2[, ...argN]],] functionBody)
  • arg1, arg2, … argN:参数列表。
  • functionBody:一个含有包括函数定义的 JavaScript 语句的字符串。
1
2
3
var myFunction = new Function("a", "b", "return a * b"); 
var x = myFunction(4, 3);
console.log(x); // 12

Lambda 函数

Lambda 函数也称之为箭头函数。

箭头函数表达式的语法比函数表达式更短。

函数只有一行语句:

1
2
var foo = (x:number)=>10 + x 
console.log(foo(100)) //输出结果为 110

编译以上代码,得到以下 JavaScript 代码:

1
2
var foo = function (x) { return 10 + x; };
console.log(foo(100)); //输出结果为 110

函数是一个语句块:

1
2
3
4
5
var foo = (x:number)=> {    
x = 10 + x
console.log(x)
}
foo(100)

我们可以不指定函数的参数类型,通过函数内来推断参数类型:

1
2
3
4
5
6
7
8
9
var func = (x)=> { 
if(typeof x=="number") {
console.log(x+" 是一个数字")
} else if(typeof x=="string") {
console.log(x+" 是一个字符串")
}
}
func(12)
func("Tom")

单个参数 () 是可选的:

1
2
3
4
var display = x => { 
console.log("输出为 "+x)
}
display(12)

函数重载

重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
定义函数重载需要定义重载签名和一个实现签名。

1
2
3
4
5
6
7
8
9
function disp(s1:string):void; 
function disp(n1:number,s1:string):void;

function disp(x:any,y?:any):void {
console.log(x);
console.log(y);
}
disp("abc")
disp(1,"xyz");

TypeScript Number

1
var num = new Number(value);

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

  1. MAX_VALUE
    

可表示的最大的数,MAX_VALUE 属性值接近于 1.79E+308。大于 MAX_VALUE 的值代表 “Infinity”。

  1. MIN_VALUE
    

可表示的最小的数,即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE,MIN_VALUE 的值约为 5e-324。小于 MIN_VALUE (“underflow values”) 的值将会转换为 0。

  1. NaN
    

非数字值(Not-A-Number)。

  1. NEGATIVE_INFINITY
    

负无穷大,溢出时返回该值。该值小于 MIN_VALUE。

  1. POSITIVE_INFINITY
    

正无穷大,溢出时返回该值。该值大于 MAX_VALUE。

  1. prototype
    

Number 对象的静态属性。使您有能力向对象添加属性和方法。

  1. constructor
    

返回对创建此对象的 Number 函数的引用。

1
2
3
4
5
console.log("TypeScript Number 属性: "); 
console.log("最大值为: " + Number.MAX_VALUE);
console.log("最小值为: " + Number.MIN_VALUE);
console.log("负无穷大: " + Number.NEGATIVE_INFINITY);
console.log("正无穷大:" + Number.POSITIVE_INFINITY);
1
2
3
4
5
TypeScript Number 属性:
最大值为: 1.7976931348623157e+308
最小值为: 5e-324
负无穷大: -Infinity
正无穷大:Infinity

NaN 实例

1
2
3
4
5
6
7
var month = 0 
if( month<=0 || month >12) {
month = Number.NaN
console.log("月份是:"+ month) // 月份是:NaN
} else {
console.log("输入月份数值正确。")
}

prototype 实例

1
2
3
4
5
6
7
8
9
10
11
function employee(id:number,name:string) { 
this.id = id
this.name = name
}

var emp = new employee(123,"admin")
employee.prototype.email = "admin@runoob.com"

console.log("员工号: "+emp.id) // 员工号: 123
console.log("员工姓名: "+emp.name) // 员工姓名: admin
console.log("员工邮箱: "+emp.email) // 员工邮箱: admin@runoob.com

Number 对象方法

方法 & 描述 实例
toExponential()
把对象的值转换为指数计数法。
1
2
3
4
//toExponential() 
var num1 = 1225.30
var val = num1.toExponential();
console.log(val) // 输出: 1.2253e+3
toFixed()
把数字转换为字符串,并对小数点指定位数。
1
2
3
4
var num3 = 177.234 
console.log("num3.toFixed() 为 "+num3.toFixed()) // 输出:177
console.log("num3.toFixed(2) 为 "+num3.toFixed(2)) // 输出:177.23
console.log("num3.toFixed(6) 为 "+num3.toFixed(6)) // 输出:177.234000
toLocaleString()
把数字转换为字符串,使用本地数字格式顺序。
1
2
var num = new Number(177.1234); 
console.log( num.toLocaleString()); // 输出:177.1234
toPrecision()
把数字格式化为指定的长度。
1
2
3
4
var num = new Number(7.123456); 
console.log(num.toPrecision()); // 输出:7.123456
console.log(num.toPrecision(1)); // 输出:7
console.log(num.toPrecision(2)); // 输出:7.1
toString()
把数字转换为字符串,使用指定的基数。数字的基数是 2 ~ 36 之间的整数。若省略该参数,则使用基数 10。
1
2
3
4
var num = new Number(10); 
console.log(num.toString()); // 输出10进制:10
console.log(num.toString(2)); // 输出2进制:1010
console.log(num.toString(8)); // 输出8进制:12
valueOf()
返回一个 Number 对象的原始数字值。
1
2
var num = new Number(10); 
console.log(num.valueOf()); // 输出:10

TypeScript String(字符串)

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

String 对象属性

属性 & 描述 实例
constructor
对创建该对象的函数的引用。
1
2
var str = new String( "This is string" ); 
console.log("str.constructor is:" + str.constructor)

输出:

1
str.constructor is:function String() { [native code] }
length
返回字符串的长度。
1
2
var uname = new String("Hello World") 
console.log("Length "+uname.length) // 输出 11
prototype
允许您向对象添加属性和方法。
1
2
3
4
5
6
7
8
9
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 方法

方法 & 描述 实例
charAt()
返回在指定位置的字符。
1
2
3
4
5
6
7
var str = new String("RUNOOB"); 
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
charCodeAt()
返回在指定的位置的字符的 Unicode 编码。
1
2
3
4
5
6
7
var str = new String("RUNOOB"); 
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
concat()
连接两个或更多字符串,并返回新的字符串。
1
2
3
4
var str1 = new String( "RUNOOB" ); 
var str2 = new String( "GOOGLE" );
var str3 = str1.concat( str2 );
console.log("str1 + str2 : "+str3) // RUNOOBGOOGLE
indexOf()
返回某个指定的字符串值在字符串中首次出现的位置。
1
2
3
4
var str1 = new String( "RUNOOB" ); 

var index = str1.indexOf( "OO" );
console.log("查找的字符串位置 :" + index ); // 3
lastIndexOf()
从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。
1
2
3
4
5
6
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
localeCompare()
用本地特定的顺序来比较两个字符串。
1
2
3
4
5
var str1 = new String( "This is beautiful string" );

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

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

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

1
2
var str="The rain in SPAIN stays mainly in the plain"; 
var n=str.match(/ain/g); // ain,ain,ain
replace()
替换与正则表达式匹配的子串
1
2
3
4
var re = /(\w+)\s(\w+)/; 
var str = "zara ali";
var newstr = str.replace(re, "$2, $1");
console.log(newstr); // ali, zara
search()
检索与正则表达式相匹配的值
1
2
3
4
5
6
7
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" );
}
slice()
提取字符串的片断,并在新的字符串中返回被提取的部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
```

</td>
</tr>

<tr>
<td> split() <br>
把字符串分割为子字符串数组。</td>
<td>

```ts
var str = "Apples are round, and apples are juicy.";
var splitted = str.split(" ", 3);
console.log(splitted) // [ 'Apples', 'are', 'round,' ]
substr()
从起始索引号提取字符串中指定数目的字符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
```

</td>
</tr>

<tr>
<td> substring() <br>
提取字符串中两个指定的索引号之间的字符。 </td>
<td>

```ts
var str = "RUNOOB GOOGLE TAOBAO FACEBOOK";
console.log("(1,2): " + str.substring(1,2)); // U
console.log("(0,10): " + str.substring(0, 10)); // RUNOOB GOO
console.log("(5): " + str.substring(5)); // B GOOGLE TAOBAO FACEBOOK
toLocaleLowerCase()
根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射。
1
2
var str = "Runoob Google"; 
console.log(str.toLocaleLowerCase( )); // runoob google
toLocaleUpperCase()
据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射。
1
2
var str = "Runoob Google"; 
console.log(str.toLocaleUpperCase( )); // RUNOOB GOOGLE
toLowerCase()
把字符串转换为小写。
1
2
var str = "Runoob Google"; 
console.log(str.toLowerCase( )); // runoob google
toString()
返回字符串。
1
2
var str = "Runoob"; 
console.log(str.toString( )); // Runoob
toUpperCase()
把字符串转换为大写。
1
2
var str = "Runoob Google"; 
console.log(str.toUpperCase( )); // RUNOOB GOOGLE
valueOf()
返回指定字符串对象的原始值。
1
2
var str = new String("Runoob"); 
console.log(str.valueOf( )); // Runoob

TypeScript Array(数组)

1
2
3
4
5
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 对象创建数组。

1
2
3
4
5
6
var arr_names:number[] = new Array(4)  

for(var i = 0; i<arr_names.length; i++) {
arr_names[i] = i * 2
console.log(arr_names[i])
}

以下实例我们直接初始化数组元素:

1
2
3
4
5
var sites:string[] = new Array("Google","Runoob","Taobao","Facebook") 

for(var i = 0;i<sites.length;i++) {
console.log(sites[i])
}

数组解构

我们也可以把数组元素赋值给变量,如下所示:

1
2
3
4
var arr:number[] = [12,13] 
var[x,y] = arr // 将数组的两个元素赋值给变量 x 和 y
console.log(x)
console.log(y)

数组迭代

1
2
3
4
5
6
var j:any; 
var nums:number[] = [1001,1002,1003,1004]

for(j in nums) {
console.log(nums[j])
}

多维数组

一个数组的元素可以是另外一个数组,这样就构成了多维数组(Multi-dimensional Array)。

1
var arr_name:datatype[][]=[ [val1,val2,val3],[v1,v2,v3] ]
1
2
3
4
5
6
7
var multi:number[][] = [[1,2,3],[23,24,25]]  
console.log(multi[0][0])
console.log(multi[0][1])
console.log(multi[0][2])
console.log(multi[1][0])
console.log(multi[1][1])
console.log(multi[1][2])

数组在函数中的使用

作为参数传递给函数

1
2
3
4
5
6
7
8
var sites:string[] = new Array("Google","Runoob","Taobao","Facebook") 

function disp(arr_sites:string[]) {
for(var i = 0;i<arr_sites.length;i++) {
console.log(arr_sites[i])
}
}
disp(sites);

作为函数的返回值

1
2
3
4
5
6
7
8
function disp():string[] { 
return new Array("Google", "Runoob", "Taobao", "Facebook");
}

var sites:string[] = disp()
for(var i in sites) {
console.log(sites[i])
}

数组方法

下表列出了一些常用的数组方法:

方法 & 描述 实例
concat()
连接两个或更多的数组,并返回结果。
1
2
3
4
5
var alpha = ["a", "b", "c"]; 
var numeric = [1, 2, 3];

var alphaNumeric = alpha.concat(numeric);
console.log("alphaNumeric : " + alphaNumeric ); // a,b,c,1,2,3
every()
检测数值元素的每个元素是否都符合条件。
1
2
3
4
5
6
7
function isBigEnough(element, index, array) { 
return (element >= 10);
}

var passed = [12, 5, 8, 130, 44].every(isBigEnough);
console.log("Test Value : " + passed ); // false

filter()
检测数值元素,并返回符合条件所有元素的数组。
1
2
3
4
5
6
function isBigEnough(element, index, array) { 
return (element >= 10);
}

var passed = [12, 5, 8, 130, 44].filter(isBigEnough);
console.log("Test Value : " + passed ); // 12,130,44
forEach()
数组每个元素都执行一次回调函数。
1
2
3
4
5
let num = [7, 8, 9];
num.forEach(function (value) {
console.log(value);
});

编译成 JavaScript 代码:

1
2
3
4
var num = [7, 8, 9];
num.forEach(function (value) {
console.log(value); // 7 8 9
});
indexOf()
搜索数组中的元素,并返回它所在的位置。
如果搜索不到,返回值 -1,代表没有此项。
1
2
var index = [12, 5, 8, 130, 44].indexOf(8); 
console.log("index is : " + index ); // 2
join()
把数组的所有元素放入一个字符串。
1
2
3
4
5
6
7
8
9
10
var arr = new Array("Google","Runoob","Taobao"); 

var str = arr.join();
console.log("str : " + str ); // Google,Runoob,Taobao

var str = arr.join(", ");
console.log("str : " + str ); // Google, Runoob, Taobao

var str = arr.join(" + ");
console.log("str : " + str ); // Google + Runoob + Taobao
lastIndexOf()
返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
1
2
var index = [12, 5, 8, 130, 44].lastIndexOf(8); 
console.log("index is : " + index ); // 2
map()
通过指定函数处理数组的每个元素,并返回处理后的数组。
1
2
3
var numbers = [1, 4, 9]; 
var roots = numbers.map(Math.sqrt);
console.log("roots is : " + roots ); // 1,2,3
pop()
删除数组的最后一个元素并返回删除的元素。
1
2
3
4
5
6
7
8
var numbers = [1, 4, 9]; 

var element = numbers.pop();
console.log("element is : " + element ); // 9

var element = numbers.pop();
console.log("element is : " + element ); // 4

push()
向数组的末尾添加一个或更多元素,并返回新的长度。
1
2
3
4
5
var numbers = new Array(1, 4, 9); 
var length = numbers.push(10);
console.log("new numbers is : " + numbers ); // 1,4,9,10
length = numbers.push(20);
console.log("new numbers is : " + numbers ); // 1,4,9,10,20
reduce()
将数组元素计算为一个值(从左到右)。
1
2
var total = [0, 1, 2, 3].reduce(function(a, b){ return a + b; }); 
console.log("total is : " + total ); // 6
reduceRight()
将数组元素计算为一个值(从右到左)。
1
2
var total = [0, 1, 2, 3].reduceRight(function(a, b){ return a + b; }); 
console.log("total is : " + total ); // 6
reverse()
反转数组的元素顺序。
1
2
var arr = [0, 1, 2, 3].reverse(); 
console.log("Reversed array is : " + arr ); // 3,2,1,0
shift()
删除并返回数组的第一个元素。
1
2
var arr = [10, 1, 2, 3].shift(); 
console.log("Shifted value is : " + arr ); // 10
slice()
选取数组的的一部分,并返回一个新数组。
1
2
3
var arr = ["orange", "mango", "banana", "sugar", "tea"]; 
console.log("arr.slice( 1, 2) : " + arr.slice( 1, 2) ); // mango
console.log("arr.slice( 1, 3) : " + arr.slice( 1, 3) ); // mango,banana
some()
检测数组元素中是否有元素符合指定条件。
1
2
3
4
5
6
7
8
9
function isBigEnough(element, index, array) { 
return (element >= 10);
}

var retval = [2, 5, 8, 1, 4].some(isBigEnough);
console.log("Returned value is : " + retval ); // false

var retval = [12, 5, 8, 1, 4].some(isBigEnough);
console.log("Returned value is : " + retval ); // true
sort()
对数组的元素进行排序。
1
2
3
var arr = new Array("orange", "mango", "banana", "sugar"); 
var sorted = arr.sort();
console.log("Returned string is : " + sorted ); // banana,mango,orange,sugar
splice()
从数组中添加或删除元素。
1
2
3
4
5
6
7
8
var arr = ["orange", "mango", "banana", "sugar", "tea"];  
var removed = arr.splice(2, 0, "water");
console.log("After adding 1: " + arr ); // orange,mango,water,banana,sugar,tea
console.log("removed is: " + removed);

removed = arr.splice(3, 1);
console.log("After removing 1: " + arr ); // orange,mango,water,sugar,tea
console.log("removed is: " + removed); // banana
toString()
把数组转换为字符串,并返回结果。
1
2
3
var arr = new Array("orange", "mango", "banana", "sugar");         
var str = arr.toString();
console.log("Returned string is : " + str ); // orange,mango,banana,sugar
unshift()
向数组的开头添加一个或更多元素,并返回新的长度。
1
2
3
4
var arr = new Array("orange", "mango", "banana", "sugar"); 
var length = arr.unshift("water");
console.log("Returned array is : " + arr ); // water,orange,mango,banana,sugar
console.log("Length of the array is : " + length ); // 5

TypeScript Map 对象

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

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

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

创建 Map

1
let myMap = new Map();
1
2
3
4
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对象中每个元素的值 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let nameSiteMapping = new Map();

// 设置 Map 对象
nameSiteMapping.set("Google", 1);
nameSiteMapping.set("Runoob", 2);
nameSiteMapping.set("Taobao", 3);

// 获取键对应的值
console.log(nameSiteMapping.get("Runoob")); // 2

// 判断 Map 中是否包含键对应的值
console.log(nameSiteMapping.has("Taobao")); // true
console.log(nameSiteMapping.has("Zhihu")); // false

// 返回 Map 对象键/值对的数量
console.log(nameSiteMapping.size); // 3

// 删除 Runoob
console.log(nameSiteMapping.delete("Runoob")); // true
console.log(nameSiteMapping);
// 移除 Map 对象的所有键/值对
nameSiteMapping.clear(); // 清除 Map
console.log(nameSiteMapping);

输出

1
2
3
4
5
6
7
2
true
false
3
true
Map { 'Google' => 1, 'Taobao' => 3 }
Map {}

迭代 Map

Map 对象中的元素是按顺序插入的,我们可以迭代 Map 对象,每一次迭代返回 [key, value] 数组。

TypeScript使用 for…of 来实现迭代:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let nameSiteMapping = new Map();

nameSiteMapping.set("Google", 1);
nameSiteMapping.set("Runoob", 2);
nameSiteMapping.set("Taobao", 3);

// 迭代 Map 中的 key
for (let key of nameSiteMapping.keys()) {
console.log(key);
}

// 迭代 Map 中的 value
for (let value of nameSiteMapping.values()) {
console.log(value);
}

// 迭代 Map 中的 key => value
for (let entry of nameSiteMapping.entries()) {
console.log(entry[0], entry[1]);
}

// 使用对象解析
for (let [key, value] of nameSiteMapping) {
console.log(key, value);
}

使用 es6 编译:

1
tsc --target es6 test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let nameSiteMapping = new Map();
nameSiteMapping.set("Google", 1);
nameSiteMapping.set("Runoob", 2);
nameSiteMapping.set("Taobao", 3);
// 迭代 Map 中的 key
for (let key of nameSiteMapping.keys()) {
console.log(key);
}
// 迭代 Map 中的 value
for (let value of nameSiteMapping.values()) {
console.log(value);
}
// 迭代 Map 中的 key => value
for (let entry of nameSiteMapping.entries()) {
console.log(entry[0], entry[1]);
}
// 使用对象解析
for (let [key, value] of nameSiteMapping) {
console.log(key, value);
}

TypeScript 元组

我们知道数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。

1
2
3
4
5
var mytuple = [10,"Runoob"];
// 或者
var mytuple = [];
mytuple[0] = 120
mytuple[1] = 234

元组运算

  • push() 向元组添加元素,添加在最后面。
  • pop() 从元组中移除元素(最后一个),并返回移除的元素。
1
2
3
4
5
6
7
8
9
var mytuple = [10,"Hello","World","typeScript"]; 
console.log("添加前元素个数:"+mytuple.length) // 返回元组的大小

mytuple.push(12) // 添加到元组中
console.log("添加后元素个数:"+mytuple.length)
console.log("删除前元素个数:"+mytuple.length)
console.log(mytuple.pop()+" 元素从元组中删除") // 删除并返回删除的元素

console.log("删除后元素个数:"+mytuple.length)

更新元组

1
2
3
4
5
6
var mytuple = [10, "Runoob", "Taobao", "Google"]; // 创建一个元组
console.log("元组的第一个元素为:" + mytuple[0])

// 更新元组元素
mytuple[0] = 121
console.log("元组中的第一个元素更新为:"+ mytuple[0])

解构元组

1
2
3
4
var a =[10,"Runoob"] 
var [b,c] = a
console.log( b ) // 10
console.log( c ) // Runoob

TypeScript 联合类型

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

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

1
2
3
4
5
var val:string|number 
val = 12
console.log("数字为 "+ val)
val = "Runoob"
console.log("字符串为 " + val)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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(["Runoob","Google","Taobao","Facebook"])

联合类型数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 = ["Runoob","Google","Taobao"]
console.log("**字符串数组**")

for(i = 0;i<arr.length;i++) {
console.log(arr[i])
}

TypeScript 接口

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)

需要注意接口不能转换为 JavaScript。 它只是 TypeScript 的一部分。

联合类型和接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface RunOptions { 
program:string;
commandline:string[]|string|(()=>string);
}

// commandline 是字符串
var options:RunOptions = {program:"test1",commandline:"Hello"};
console.log(options.commandline)

// commandline 是字符串数组
options = {program:"test1",commandline:["Hello","World"]};
console.log(options.commandline[0]);
console.log(options.commandline[1]);

// commandline 是一个函数表达式
options = {program:"test1",commandline:()=>{return "**Hello World**";}};

var fn:any = options.commandline;
console.log(fn());

接口和数组

接口中我们可以将数组的索引值和元素设置为不同类型,索引值可以是数字或字符串。

设置元素为字符串类型:

1
2
3
4
5
6
7
8
interface namelist { 
[index:number]:string
}

// 类型一致,正确
var list2:namelist = ["Google","Runoob","Taobao"]
// 错误元素 1 不是 string 类型
// var list2:namelist = ["Runoob",1,"Taobao"]

接口继承

接口继承就是说接口可以通过其他接口来扩展自己。

Typescript 允许接口继承多个接口。

继承使用关键字 extends。

单接口继承语法格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Person { 
age:number
}

interface Musician extends Person {
instrument:string
}

var drummer = <Musician>{};
drummer.age = 27
drummer.instrument = "Drums"
console.log("年龄: "+drummer.age)
console.log("喜欢的乐器: "+drummer.instrument)

多继承实例

1
2
3
4
5
6
7
8
9
10
11
interface IParent1 { 
v1:number
}

interface IParent2 {
v2:number
}

interface Child extends IParent1, IParent2 { }
var Iobj:Child = { v1:12, v2:23}
console.log("value 1: "+Iobj.v1+" value 2: "+Iobj.v2)

TypeScript 类

TypeScript 是面向对象的 JavaScript。

类描述了所创建的对象共同的属性和方法。

TypeScript 支持面向对象的所有特性,比如 类、接口等。

TypeScript 类定义方式如下:

1
2
3
class class_name { 
// 类作用域
}

定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块(类的数据成员):

  • 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。
  • 构造函数 − 类实例化时调用,可以为类的对象分配内存。
  • 方法 − 方法为对象要执行的操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Car { 
// 字段
engine:string;

// 构造函数
constructor(engine:string) {
this.engine = engine
}

// 方法
disp():void {
console.log("发动机为 : "+this.engine)
}
}

创建实例化对象

1
2
3
4
5
6
7
var obj = new Car("Engine 1")

// 访问属性
obj.field_name

// 访问方法
obj.function_name()

类的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Shape { 
Area:number

constructor(a:number) {
this.Area = a
}
}

class Circle extends Shape {
disp():void {
console.log("圆的面积: "+this.Area)
}
}

var obj = new Circle(223);
obj.disp()

需要注意的是子类只能继承一个父类,TypeScript 不支持继承多个类,但支持多重继承,如下实例:

1
2
3
4
5
6
7
8
9
10
class Root { 
str:string;
}

class Child extends Root {}
class Leaf extends Child {} // 多重继承,继承了 Child 和 Root 类

var obj = new Leaf();
obj.str ="hello"
console.log(obj.str)

继承类的方法重写

类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。
其中 super 关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
class PrinterClass { 
doPrint():void {
console.log("父类的 doPrint() 方法。")
}
}

class StringPrinter extends PrinterClass {
doPrint():void {
super.doPrint() // 调用父类的函数
console.log("子类的 doPrint()方法。")
}
}

static 关键字

static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。

1
2
3
4
5
6
7
8
9
10
class StaticMem {  
static num:number;

static disp():void {
console.log("num 值为 "+ StaticMem.num)
}
}

StaticMem.num = 12 // 初始化静态变量
StaticMem.disp() // 调用静态方法

instanceof 运算符

instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。

1
2
3
4
class Person{ } 
var obj = new Person()
var isPerson = obj instanceof Person;
console.log("obj 对象是 Person 类实例化来的吗? " + isPerson); // obj 对象是 Person 类实例化来的吗? true

访问控制修饰符

TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限。

  • public(默认) : 公有,可以在任何地方被访问。
  • protected : 受保护,可以被其自身以及其子类访问。
  • private : 私有,只能被其定义所在的类访问。

以下实例定义了两个变量 str1 和 str2,str1 为 public,str2 为 private,实例化后可以访问 str1,如果要访问 str2 则会编译错误。

1
2
3
4
5
6
7
8
class Encapsulate { 
str1:string = "hello"
private str2:string = "world"
}

var obj = new Encapsulate()
console.log(obj.str1) // 可访问
console.log(obj.str2) // 编译错误, str2 是私有的

类和接口

类可以实现接口,使用关键字 implements,并将 interest 字段作为类的属性使用。
以下实例中 AgriLoan 类实现了 ILoan 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface ILoan { 
interest:number
}

class AgriLoan implements ILoan {
interest:number
rebate:number

constructor(interest:number,rebate:number) {
this.interest = interest
this.rebate = rebate
}
}

var obj = new AgriLoan(10,1)
console.log("利润为 : "+obj.interest+",抽成为 : "+obj.rebate ) // 利润为 : 10,抽成为 : 1

TypeScript 对象

1
2
3
4
5
6
7
var sites = { 
site1:"Runoob",
site2:"Google"
};
// 访问对象的值
console.log(sites.site1)
console.log(sites.site2)

此外对象也可以作为一个参数传递给函数,如下实例:

1
2
3
4
5
6
7
8
9
var sites = { 
site1:"Runoob",
site2:"Google",
};
var invokesites = function(obj: { site1:string, site2 :string }) {
console.log("site1 :"+obj.site1)
console.log("site2 :"+obj.site2)
}
invokesites(sites)

鸭子类型(Duck Typing)

鸭子类型(英语:duck typing)是动态类型的一种风格,是多态(polymorphism)的一种形式。

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

在鸭子类型中,关注点在于对象的行为能做什么,而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为”鸭子”的对象,并调用它的”走”和”叫”方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的”走”和”叫”方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的”走”和”叫”方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface IPoint { 
x:number
y:number
}
function addPoints(p1:IPoint,p2:IPoint):IPoint {
var x = p1.x + p2.x
var y = p1.y + p2.y
return {x:x,y:y}
}

// 正确
var newPoint = addPoints({x:3,y:4},{x:5,y:1})

// 错误
var newPoint2 = addPoints({x:1},{x:4,y:3})

TypeScript 命名空间

IShape.ts 文件代码:

1
2
3
4
5
namespace Drawing { 
export interface IShape {
draw();
}
}

Circle.ts 文件代码:

1
2
3
4
5
6
7
8
/// <reference path = "IShape.ts" /> 
namespace Drawing {
export class Circle implements IShape {
public draw() {
console.log("Circle is drawn");
}
}
}

Triangle.ts 文件代码:

1
2
3
4
5
6
7
8
/// <reference path = "IShape.ts" /> 
namespace Drawing {
export class Triangle implements IShape {
public draw() {
console.log("Triangle is drawn");
}
}
}

TestShape.ts 文件代码:

1
2
3
4
5
6
7
8
/// <reference path = "IShape.ts" />   
/// <reference path = "Circle.ts" />
/// <reference path = "Triangle.ts" />
function drawAllShapes(shape:Drawing.IShape) {
shape.draw();
}
drawAllShapes(new Drawing.Circle());
drawAllShapes(new Drawing.Triangle());

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

1
tsc --out app.js TestShape.ts  

输出

1
2
3
$ node app.js
Circle is drawn
Triangle is drawn

嵌套命名空间

命名空间支持嵌套,即你可以将命名空间定义在另外一个命名空间里头。

Invoice.ts 文件代码:

1
2
3
4
5
6
7
8
9
namespace Runoob { 
export namespace invoiceApp {
export class Invoice {
public calculateDiscount(price: number) {
return price * .40;
}
}
}
}

InvoiceTest.ts 文件代码:

1
2
3
/// <reference path = "Invoice.ts" />
var invoice = new Runoob.invoiceApp.Invoice();
console.log(invoice.calculateDiscount(500));

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

1
tsc --out app.js InvoiceTest.ts

TypeScript 模块

TypeScript 模块的设计理念是可以更换的组织代码。

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

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

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

此外还有有 SystemJs 和 Webpack。

模块导出使用关键字 export 关键字,语法格式如下:

1
2
3
4
// 文件名 : SomeInterface.ts 
export interface SomeInterface {
// 代码部分
}

要在另外一个文件使用该模块就需要使用 import 关键字来导入:

1
import someInterfaceRef = require("./SomeInterface");

实例:
IShape.ts 文件代码:

1
2
3
4
/// <reference path = "IShape.ts" /> 
export interface IShape {
draw();
}

Circle.ts 文件代码:

1
2
3
4
5
6
7
import shape = require("./IShape"); 
export class Circle implements shape.IShape {
public draw() {
console.log("Cirlce is drawn (external module)");
}
}

Triangle.ts 文件代码:

1
2
3
4
5
6
import shape = require("./IShape"); 
export class Triangle implements shape.IShape {
public draw() {
console.log("Triangle is drawn (external module)");
}
}

TestShape.ts 文件代码:

1
2
3
4
5
6
7
8
9
import shape = require("./IShape"); 
import circle = require("./Circle");
import triangle = require("./Triangle");

function drawAllShapes(shapeToDraw: shape.IShape) {
shapeToDraw.draw();
}
drawAllShapes(new circle.Circle());
drawAllShapes(new triangle.Triangle());

TypeScript 声明文件

TypeScript 作为 JavaScript 的超集,在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了。

假如我们想使用第三方库,比如 jQuery,我们通常这样获取一个 id 是 foo 的元素:

1
2
3
$('#foo');
// 或
jQuery('#foo');

但是在 TypeScript 中,我们并不知道 $ 或 jQuery 是什么东西
这时,我们需要使用 declare 关键字来定义它的类型,帮助 TypeScript 判断我们传入的参数类型对不对:

1
2
3
declare var jQuery: (selector: string) => any;

jQuery('#foo');

declare 定义的类型只会用于编译时的检查,编译结果中会被删除。

上例的编译结果是:

1
jQuery('#foo');

声明文件
声明文件以 .d.ts 为后缀,例如:

1
runoob.d.ts

声明文件或模块的语法格式如下:

1
2
declare module Module_Name {
}

TypeScript 引入声明文件语法格式:

1
/// <reference path = " runoob.d.ts" />

当然,很多流行的第三方库的声明文件不需要我们定义了,比如 jQuery 已经有人帮我们定义好了:jQuery in DefinitelyTyped。

以下定义一个第三方库来演示:
CalcThirdPartyJsLib.js 文件代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Runoob;  
(function(Runoob) {
var Calc = (function () {
function Calc() {
}
})
Calc.prototype.doSum = function (limit) {
var sum = 0;

for (var i = 0; i <= limit; i++) {
sum = sum + i;
}
return sum;
}
Runoob.Calc = Calc;
return Calc;
})(Runoob || (Runoob = {}));
var test = new Runoob.Calc();

如果我们想在 TypeScript 中引用上面的代码,则需要设置声明文件 Calc.d.ts,代码如下:

1
2
3
4
5
declare module Runoob { 
export class Calc {
doSum(limit:number) : number;
}
}

声明文件不包含实现,它只是类型声明,把声明文件加入到 TypeScript 中:
CalcTest.ts 文件代码:

1
2
3
4
/// <reference path = "Calc.d.ts" /> 
var obj = new Runoob.Calc();
// obj.doSum("Hello"); // 编译错误
console.log(obj.doSum(10));

使用 tsc 命令来编译以上代码文件:

1
tsc CalcTest.ts

最后我们编写一个 runoob.html 文件,引入 CalcTest.js 文件及第三方库 CalcThirdPartyJsLib.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<script src = "CalcThirdPartyJsLib.js"></script>
<script src = "CalcTest.js"></script>
</head>
<body>
<h1>声明文件测试</h1>
<p>菜鸟测试一下。</p>
</body>
</html>

浏览器打开该文件输出结果如下:

TypeScript 测验

TypeScript 测验

QA 记录

您可以通过启用编译器选项来禁用隐式变量类型赋值,该选项是?

noImplicitAny

TypeScript 始终可以正确推断变量类型?

正确

您可以通过启用哪个编译器属性来启用“未定义 undefined”和“空 null”类型?

strictNullChecks

以下哪个选项类似于 ‘any’,但在不确定类型时更安全。

unknown

‘readonly’ 访问修饰符对数组变量赋值有什么作用?

TypeScript 始终能够正确推断出数组的类型?

错误

类型别名通常与什么类型一起使用?

Strings

接口类似于类型别名,但仅适用于对象类型?

错误

keyof 关键字可以与索引名一起使用来提取索引类型?

正确

不能为泛型分配默认值?

错误

以下代码参数的类型是什么?

1
function ex(param1?: string){}

string | undefined

当一个类扩展另一个类,并替换其父类的成员时,它被称为什么?

override (覆盖)

-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道