TypeScript

目录

TypeScript 简介

语言特性

JavaScript 与 TypeScript 的区别

TypeScript 基础语法

第一个 TypeScript 程序

TypeScript 保留关键字

空白和换行

TypeScript 区分大小写

分号是可选的

TypeScript 注释

TypeScript 支持两种类型的注释

TypeScript 与面向对象

TypeScript 基础类型

Any 类型

Null 和 Undefined

null

undefined

never 类型

TypeScript 变量声明

类型断言(Type Assertion)

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

类型推断

变量作用域

TypeScript 运算符

算术运算符

关系运算符

逻辑运算符

短路运算符(&& 与 ||)

位运算符

赋值运算符

三元运算符 (?)

类型运算符

typeof 运算符

instanceof

TypeScript 循环

for...in 循环

语法

实例

for…of 、forEach、every 和 some 循环

break 语句

continue 语句

无限循环

TypeScript 函数

函数定义

函数返回值

带参数函数

可选参数和默认参数

可选参数

默认参数

剩余参数

匿名函数

实例

匿名函数自调用

构造函数

递归函数

Lambda 函数

实例

函数重载

实例

TypeScript Array(数组)

Array 对象

实例

数组解构

数组迭代

多维数组

实例

数组在函数中的使用

作为参数传递给函数

数组方法

TypeScript Map 对象

创建 Map

迭代 Map

TypeScript 元组

实例

访问元组

实例

元组运算

更新元组

解构元组

TypeScript 联合类型

实例

联合类型数组

TypeScript 接口

实例

联合类型和接口

接口和数组

接口继承

单继承实例

多继承实例

TypeScript 类

实例

创建类的数据成员

创建实例化对象

完整实例

类的继承

实例

继承类的方法重写

static 关键字

instanceof 运算符

访问控制修饰符

类和接口

TypeScript 对象

对象实例

TypeScript 类型模板

鸭子类型(Duck Typing)


TypeScript 简介

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。

TypeScript 由微软开发的自由和开源的编程语言。

TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

语言特性

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

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

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

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

JavaScript 与 TypeScript 的区别

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

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

TypeScript 基础语法

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

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

第一个 TypeScript 程序

我们可以使用以下 TypeScript 程序来输出 "Hello World" :        

const hello : string = "Hello World!"
console.log(hello)

以上代码首先通过 tsc 命令编译:

tsc first.ts

得到如下 js 代码:

var hello = "Hello World!";
console.log(hello);

最后我们使用 node 命令来执行该 js 代码。

$ node first.js
Hello World

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

tsc file1.ts file2.ts file3.ts

 tsc常用编译参数如下:

序号编译参数说明
1.

--help

显示帮助信息

2.

--module

载入扩展模块

3.

--target

设置 ECMA 版本

4.

--declaration

额外生成一个 .d.ts 扩展名的文件。

tsc ts-hw.ts --declaration

以上命令会生成 ts-hw.d.ts、ts-hw.js 两个文件。

5.

--removeComments

删除文件的注释

6.

--out

编译多个文件并合并到一个输出的文件

7.

--sourcemap

生成一个 sourcemap (.map) 文件。

sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。

8.

--module noImplicitAny

在表达式和声明上有隐含的 any 类型时报错

9.

--watch

在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。

TypeScript 保留关键字

TypeScript 保留关键字如下表所示:

breakascatchswitch
caseifthrowelse
varnumberstringget
moduletypeinstanceoftypeof
publicprivateenumexport
finallyforwhilevoid
nullsuperthisnew
inreturntruefalse
anyextendsstaticlet
packageimplementsinterfacefunction
dotryyieldconst
continue

空白和换行

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

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

TypeScript 区分大小写

TypeScript 区分大写和小写字符。

分号是可选的

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

以下代码都是合法的:

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

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

console.log("Bing");console.log("Google");

TypeScript 注释

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

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

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

编译器会忽略注释。

TypeScript 支持两种类型的注释

  • 单行注释 ( // ) − 在 // 后面的文字都是注释内容。

  • 多行注释 (/* */) − 这种注释可以跨越多行。

// 这是一个单行注释
 
/* 
 这是一个多行注释 
 这是一个多行注释 
 这是一个多行注释 
*/

TypeScript 与面向对象

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

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

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

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

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

TypeScript 面向对象编程实例:

class Site { 
   name():void { 
      console.log("Runoob") 
   } 
} 
var obj = new Site(); 
obj.name();

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

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

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

var Site = /** @class */ (function () {
    function Site() {
    }
    Site.prototype.name = function () {
        console.log("Noodles");
    };
    return Site;
}());
var obj = new Site();
obj.name();

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

Noodlse

TypeScript 基础类型

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

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

双精度 64 位浮点值。它可以用来表示整数和分数。

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

一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式。

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

表示逻辑值:true 和 false。

let flag: boolean = true;
数组类型

声明变量为数组。

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

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

元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。

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

枚举类型用于定义数值集合。

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

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

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

表示对象值缺失。

undefinedundefined

用于初始化变量为一个未定义的值

nevernever

never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。

Any 类型

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

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

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

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

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

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

let arrayList: any[] = [1, false, 'fine'];
arrayList[1] = 100;

Null 和 Undefined

null

在 JavaScript 中 null 表示 "什么都没有"。

null是一个只有一个值的特殊类型。表示一个空对象引用。

用 typeof 检测 null 返回是 object。

undefined

在 JavaScript 中, undefined 是一个没有设置值的变量。

typeof 一个没有值的变量会返回 undefined。

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

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

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

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

never 类型

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

let x: never;
let y: number;

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

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

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

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

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

TypeScript 变量声明

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

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

TypeScript 变量的命名规则:

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

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

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

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

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

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

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

例如:

var uname:string = "Noodles";

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

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

例如:

var uname:string;

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

var [变量名] = 值;

例如:

var uname = "Noodles";

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

var [变量名];

例如:

var uname;

实例:

var uname:string = "Noodles";
var score1:number = 50;
var score2:number = 42.50
var sum = score1 + score2
console.log("名字: "+uname)
console.log("第一个科目成绩: "+score1)
console.log("第二个科目成绩: "+score2)
console.log("总成绩: "+sum)

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

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

var uname = "Noodles";
var score1 = 50;
var score2 = 42.50;
var sum = score1 + score2;
console.log("名字: " + uname);
console.log("第一个科目成绩: " + score1);
console.log("第二个科目成绩: " + score2);
console.log("总成绩: " + sum);

执行该 JavaScript 代码输出结果为:

名字: Noodles
第一个科目成绩: 50
第二个科目成绩: 42.5
总成绩: 92.5

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

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

类型断言(Type Assertion)

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

语法格式:

<类型>值

值 as 类型

 实例:

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

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

当 S 类型是 T 类型的子集,或者 T 类型是 S 类型的子集时,S 能被成功断言成 T。这是为了在进行类型断言时提供额外的安全性,完全毫无根据的断言是危险的,如果你想这么做,你可以使用 any。

它之所以不被称为类型转换,是因为转换通常意味着某种运行时的支持。但是,类型断言纯粹是一个编译时语法,同时,它也是一种为编译器提供关于如何分析代码的方法。

编译后,以上代码会生成如下 JavaScript 代码:

var str = '1';
var str2 = str;  //str、str2 是 string 类型
console.log(str2);

执行输出结果:

1

类型推断

当类型没有给出时,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 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 运算符

运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算。

考虑以下计算:

7 + 5 = 12

以上实例中 7、5 和 12 是操作数。

运算符 + 用于加值。

运算符 = 用于赋值。

TypeScript 主要包含以下几种运算:

  • 算术运算符
  • 逻辑运算符
  • 关系运算符
  • 按位运算符
  • 赋值运算符
  • 三元/条件运算符
  • 字符串运算符
  • 类型运算符

算术运算符

假定 y=5,下面的表格解释了这些算术运算符的操作:

运算符描述例子x 运算结果y 运算结果
+加法x=y+275
-减法x=y-235
*乘法x=y*2105
/除法x=y/22.55
%取模(余数)x=y%215
++自增x=++y66
x=y++56
--自减x=--y44
x=y--54

 实例:

var num1:number = 10
var num2:number = 2
var res:number = 0
   
res = num1 + num2
console.log("加:        "+res);

res = num1 - num2;
console.log("减: "+res)

res = num1*num2
console.log("乘:    "+res)

res = num1/num2
console.log("除:   "+res)
   
res = num1%num2
console.log("余数:   "+res)

num1++
console.log("num1 自增运算: "+num1)

num2--
console.log("num2 自减运算: "+num2)

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

var num1 = 10;
var num2 = 2;
var res = 0;
res = num1 + num2;
console.log("加:        " + res);
res = num1 - num2;
console.log("减: " + res);
res = num1 * num2;
console.log("乘:    " + res);
res = num1 / num2;
console.log("除:   " + res);
res = num1 % num2;
console.log("余数:   " + res);
num1++;
console.log("num1 自增运算: " + num1);
num2--;
console.log("num2 自减运算: " + num2);

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

加:        12
减: 8
乘:    20
除:   5
余数:   0
num1 自增运算: 11
num2 自减运算: 1

关系运算符

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

x=5,下面的表格解释了关系运算符的操作:

运算符描述比较返回值
==等于x==8false
x==5true
!= 不等于x!=8true
> 大于x>8false
< 小于x<8true
>= 大于或等于x>=8false
<= 小于或等于x<=8true

逻辑运算符

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

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

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

实例:

var avg:number = 20; 
var percentage:number = 90; 
 
console.log("avg 值为: "+avg+" ,percentage 值为: "+percentage);
    
var res:boolean = ((avg>50)&&(percentage>80)); 
console.log("(avg>50)&&(percentage>80): ",res);
 
var res:boolean = ((avg>50)||(percentage>80)); 
console.log("(avg>50)||(percentage>80): ",res);
 
var res:boolean=!((avg>50)&&(percentage>80)); 
console.log("!((avg>50)&&(percentage>80)): ",res);

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

var avg = 20;
var percentage = 90;
console.log("avg 值为: " + avg + " ,percentage 值为: " + percentage);
var res = ((avg > 50) && (percentage > 80));
console.log("(avg>50)&&(percentage>80): ", res);
var res = ((avg > 50) || (percentage > 80));
console.log("(avg>50)||(percentage>80): ", res);
var res = !((avg > 50) && (percentage > 80));
console.log("!((avg>50)&&(percentage>80)): ", res);

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

avg 值为: 20 ,percentage 值为: 90
(avg>50)&&(percentage>80):  false
(avg>50)||(percentage>80):  true
!((avg>50)&&(percentage>80)):  true

短路运算符(&& 与 ||)

&& 与 || 运算符可用于组合表达式。 && 运算符只有在左右两个表达式都为 true 时才返回 true。

考虑以下实例:

var a = 10 
var result = ( a<10 && a>5)

以上实例中 a < 10 与 a > 5 是使用了 && 运算符的组合表达式,第一个表达式返回了 false,由于 && 运算需要两个表达式都为 true,所以如果第一个为 false,就不再执行后面的判断(a > 5 跳过计算),直接返回 false。

|| 运算符只要其中一个表达式为 true ,则该组合表达式就会返回 true。

考虑以下实例:

var a = 10 
var result = ( a>5 || a<10)

以上实例中 a > 5 与 a < 10 是使用了 || 运算符的组合表达式,第一个表达式返回了 true,由于 || 组合运算只需要一个表达式为 true,所以如果第一个为 true,就不再执行后面的判断(a < 10 跳过计算),直接返回 true。

位运算符

位操作是程序设计中对位模式按位或二进制数的一元和二元操作。

运算符描述例子类似于结果十进制
&AND,按位与处理两个长度相同的二进制数,两个相应的二进位都为 1,该位的结果值才为 1,否则为 0。x = 5 & 10101 & 00010001 1
|OR,按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为 1,该位的结果值为 1。x = 5 | 10101 | 00010101 5
~取反,取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字 1 成为 0,0 成为 1。x = ~ 5 ~01011010 -6
^异或,按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为 1,否则该位为 0。x = 5 ^ 10101 ^ 00010100 4
<<左移,把 << 左边的运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0。x = 5 << 10101 << 11010 10
>>右移,把 >> 左边的运算数的各二进位全部右移若干位,>> 右边的数指定移动的位数。x = 5 >> 10101 >> 100102
>>>无符号右移,与有符号右移位类似,除了左边一律使用0 补位。x = 2 >>> 10010 >>> 100011

赋值运算符

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

给定 x=10  y=5,下面的表格解释了赋值运算符:

运算符例子实例x 值
= (赋值)x = yx = yx = 5
+= (先进行加运算后赋值)x += yx = x + yx = 15
-= (先进行减运算后赋值)x -= yx = x - yx = 5
*= (先进行乘运算后赋值)x *= yx = x * yx = 50
/= (先进行除运算后赋值)x /= yx = x / yx = 2

三元运算符 (?)

三元运算有 3 个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。

Test ? expr1 : expr2
  • Test − 指定的条件语句
  • expr1 − 如果条件语句 Test 返回 true 则返回该值
  • expr2 − 如果条件语句 Test 返回 false 则返回该值

以下实例: 

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

实例中用于判断变量是否大于 0。

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

var num = -2;
var result = num > 0 ? "大于 0" : "小于 0,或等于 0";
console.log(result);

以上实例输出结果如下:

小于 0,或等于 0

类型运算符

typeof 运算符

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

查看以下实例:        

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

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

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

instanceof

instanceof 运算符用于判断对象是否为指定的类型,后面会具体介绍它。


TypeScript 循环

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

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

for...in 循环

for...in 语句用于一组值的集合或列表进行迭代输出。

语法

语法格式如下所示:

for (var val in list) { 
    //语句 
}

 val 需要为 string 或 any 类型。

实例

var j:any; 
var n:any = "a b c" 
 
for(j in n) {
    console.log(n[j])  
}

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

var j;
var n = "a b c";
for (j in n) {
    console.log(n[j]);
}
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(集合)等可迭代的数据结构等。

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。

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

break 语句

break 语句有以下两种用法:

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

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

continue 语句

continue 语句有点像 break 语句。但它不是强制终止,continue 会跳过当前循环中的代码,强迫开始下一次循环。

对于 for 循环,continue 语句执行后自增语句仍然会执行。对于 while 和 do...while 循环,continue 语句重新执行条件判断语句。

无限循环

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

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

for(;;) { 
   // 语句
}

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

while(true) { 
   // 语句
} 

TypeScript 函数

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

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

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

函数定义

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

语法格式如下所示:

function test() {   // 函数定义
    console.log("调用函数") 
} 
test()              // 调用函数

函数返回值

有时,我们会希望函数将执行的结果返回到调用它的地方。

通过使用 return 语句就可以实现。

在使用 return 语句时,函数会停止执行,并返回指定的值。

语法格式如下所示:

function function_name():return_type { 
    // 语句
    return value; 
}
  • return_type 是返回值的类型。

  • return 关键词后跟着要返回的结果。

  • 一般情况下,一个函数只有一个 return 语句。

  • 返回值的类型需要与函数定义的返回类型(return_type)一致。

// 函数定义
function greet():string { // 返回一个字符串
    return "Hello World" 
} 
 
function caller() { 
    var msg = greet() // 调用 greet() 函数 
    console.log(msg) 
} 
 
// 调用函数
caller()
  • 实例中定义了函数 greet(),返回值的类型为 string。

  • greet() 函数通过 return 语句返回给调用它的地方,即变量 msg,之后输出该返回值。。

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

// 函数定义
function greet() {
    return "Hello World";
}
function caller() {
    var msg = greet(); // 调用 greet() 函数 
    console.log(msg);
}
// 调用函数
caller();

带参数函数

在调用函数时,您可以向其传递值,这些值被称为参数。

这些参数可以在函数中使用。

您可以向函数发送多个参数,每个参数使用逗号 , 分隔:

语法格式如下所示:

function func_name( param1 [:datatype], param2 [:datatype]) {   
}
  • param1、param2 为参数名。

  • datatype 为参数类型。

function add(x: number, y: number): number {
    return x + y;
}
console.log(add(1,2))
  • 实例中定义了函数 add(),返回值的类型为 number。

  • add() 函数中定义了两个 number 类型的参数,函数内将两个参数相加并返回。

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

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

可选参数和默认参数

可选参数

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

实例

function buildName(firstName: string, lastName: string) {
    return firstName + " " + lastName;
}
 
let result1 = buildName("Bob");                  // 错误,缺少参数
let result2 = buildName("Bob", "Adams", "Sr.");  // 错误,参数太多了
let result3 = buildName("Bob", "Adams");         // 正确

以下实例,我们将 lastName 设置为可选参数:

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 放在后面。

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

默认参数

我们也可以设置参数的默认值,这样在调用函数的时候,如果不传入该参数的值,则使用默认参数,语法格式为:

function function_name(param1[:type],param2[:type] = default_value) { 
}

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

实例

以下实例函数的参数 rate 设置了默认值为 0.50,调用该函数时如果未传入参数则使用该默认值:

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

输出结果:

计算结果:  500
计算结果:  300

剩余参数

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

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

function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}
  
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

函数的最后一个命名参数 restOfName 以 ... 为前缀,它将成为一个由剩余参数组成的数组,索引值从0(包括)到 restOfName.length(不包括)。

匿名函数

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

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

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

语法格式如下:

var res = function( [arguments] ) { ... }

实例

不带参数匿名函数:

var msg = function() { 
    return "hello world";  
} 
console.log(msg())

带参数匿名函数:

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

匿名函数自调用

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

(function () { 
    var x = "Hello!!";   
    console.log(x)     
 })()

输出结果:

Hello!!

构造函数

TypeScript 也支持使用 JavaScript 内置的构造函数 Function() 来定义函数:

语法格式如下:

var res = new Function ([arg1[, arg2[, ...argN]],] functionBody)

参数说明:

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

递归函数

递归函数即在函数内调用函数本身。

举个例子:
从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?"从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?'从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……'"

function factorial(number) {
    if (number <= 0) {         // 停止执行
        return 1; 
    } else {     
        return (number * factorial(number - 1));     // 调用自身
    } 
}; 
console.log(factorial(6));      // 输出 720

Lambda 函数

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

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

函数只有一行语句:

( [param1, param2,…param n] )=>statement;

实例

以下实例声明了 lambda 表达式函数,函数返回两个数的和:

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

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

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

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

var func = (x)=> { 
    if(typeof x=="number") { 
        console.log(x+" 是一个数字") 
    } else if(typeof x=="string") { 
        console.log(x+" 是一个字符串") 
    }  
} 
func(12) 
func("Tom")

单个参数 () 是可选的:

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

无参数时可以设置空括号:

var disp =()=> { 
    console.log("Function invoked"); 
} 
disp();

函数重载

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

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

参数类型不同:

function disp(string):void; 
function disp(number):void;

参数数量不同:

function disp(n1:number):void; 
function disp(x:number,y:number):void;

参数类型顺序不同:

function disp(n1:number,s1:string):void; 
function disp(s:string,n:number):void;

如果参数类型不同,则参数类型应设置为 any

参数数量不同你可以将不同的参数设置为可选。

实例

以下实例定义了参数类型与参数数量不同:

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");

输出结果:

abc
undefined
1
xyz

TypeScript Array(数组)

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

数组非常常用。

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

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

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

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

Array 对象

我们也可以使用 Array 对象创建数组。

Array 对象的构造函数接受以下两种值:

  • 表示数组大小的数值。
  • 初始化的数组列表,元素使用逗号分隔值。

实例

指定数组初始化大小:

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]) 
}

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

var sites:string[] = new Array("Google","Runoob","Taobao","Facebook") 
 
for(var i = 0;i<sites.length;i++) { 
        console.log(sites[i]) 
}

数组解构

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

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

数组迭代

我们可以使用 for 语句来循环输出数组的各个元素:

var j:any; 
var nums:number[] = [1001,1002,1003,1004] 
 
for(j in nums) { 
    console.log(nums[j]) 
}

多维数组

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

最简单的多维数组是二维数组,定义方式如下:

var arr_name:datatype[][]=[ [val1,val2,val3],[v1,v2,v3] ]

实例

定义一个二维数组,每一个维度的数组有三个元素。

var multi:number[][] = [[1,2,3],[4,5,6]]  
console.log(multi[0][0])  //1
console.log(multi[0][1])  //2
console.log(multi[0][2])  //3
console.log(multi[1][0])  //4
console.log(multi[1][1])  //5
console.log(multi[1][2])  //6

数组在函数中的使用

作为参数传递给函数

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);

 作为函数的返回值:

function disp():string[] { 
        return new Array("Google", "Runoob", "Taobao", "Facebook");
} 
 
var sites:string[] = disp() 
for(var i in sites) { 
        console.log(sites[i]) 
}

数组方法

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

序号方法 & 描述实例
1.concat()

连接两个或更多的数组,并返回结果。

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   
2.every()

检测数值元素的每个元素是否都符合条件。

function isBigEnough(element, index, array) { 
        return (element >= 10); 
} 
        
var passed = [12, 5, 8, 130, 44].every(isBigEnough); 
console.log("Test Value : " + passed ); // false
3.filter()

检测数值元素,并返回符合条件所有元素的数组。

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
4.forEach()

数组每个元素都执行一次回调函数。

let num = [7, 8, 9];
num.forEach(function (value) {
    console.log(value);
}); 

编译成 JavaScript 代码:

var num = [7, 8, 9];
num.forEach(function (value) {
    console.log(value);  // 7   8   9
});
5.indexOf()

搜索数组中的元素,并返回它所在的位置。

如果搜索不到,返回值 -1,代表没有此项。

var index = [12, 5, 8, 130, 44].indexOf(8); 
console.log("index is : " + index );  // 2
6.join()

把数组的所有元素放入一个字符串。

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
7.lastIndexOf()

返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。

var index = [12, 5, 8, 130, 44].lastIndexOf(8); 
console.log("index is : " + index );  // 2
8.map()

通过指定函数处理数组的每个元素,并返回处理后的数组。

var numbers = [1, 4, 9]; 
var roots = numbers.map(Math.sqrt); 
console.log("roots is : " + roots );  // 1,2,3
9.pop()

删除数组的最后一个元素并返回删除的元素。

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
10.push()

向数组的末尾添加一个或更多元素,并返回新的长度。

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
11.reduce()

将数组元素计算为一个值(从左到右)。

var total = [0, 1, 2, 3].reduce(function(a, b){ return a + b; }); 
console.log("total is : " + total );  // 6
12.reduceRight()

将数组元素计算为一个值(从右到左)。

var total = [0, 1, 2, 3].reduceRight(function(a, b){ return a + b; }); 
console.log("total is : " + total );  // 6
13.reverse()

反转数组的元素顺序。

var arr = [0, 1, 2, 3].reverse(); 
console.log("Reversed array is : " + arr );  // 3,2,1,0
14.shift()

删除并返回数组的第一个元素。

var arr = [10, 1, 2, 3].shift(); 
console.log("Shifted value is : " + arr );  // 10
15.slice()

选取数组的的一部分,并返回一个新数组。

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
16.some()

检测数组元素中是否有元素符合指定条件。

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
17.sort()

对数组的元素进行排序。

var arr = new Array("orange", "mango", "banana", "sugar"); 
var sorted = arr.sort(); 
console.log("Returned string is : " + sorted );  // banana,mango,orange,sugar
18.splice()

从数组中添加或删除元素。

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
19.toString()

把数组转换为字符串,并返回结果。

var arr = new Array("orange", "mango", "banana", "sugar");         
var str = arr.toString(); 
console.log("Returned string is : " + str );  // orange,mango,banana,sugar
20.unshift()

向数组的开头添加一个或更多元素,并返回新的长度。

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 中引入的一种新的数据结构

创建 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对象中每个元素的值 。
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);

 使用 es6 编译:

tsc --target es6 test.ts
let nameSiteMapping = new Map();
// 设置 Map 对象
nameSiteMapping.set("Google", 1);
nameSiteMapping.set("Runoob", 2);
nameSiteMapping.set("Taobao", 3);
// 获取键对应的值
console.log(nameSiteMapping.get("Runoob")); //40
// 判断 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);

迭代 Map

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

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

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[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。

元组中允许存储不同类型的元素,元组可以作为参数传递给函数。

创建元组的语法格式如下:

var tuple_name = [value1,value2,value3,…value n]

实例

声明一个元组并初始化:

var mytuple = [10,"Runoob"];
或者我们可以先声明一个空元组,然后再初始化:
var mytuple = []; 
mytuple[0] = 120 
mytuple[1] = 234

访问元组

元组中元素使用索引来访问,第一个元素的索引值为 0,第二个为 1,以此类推第 n 个为 n-1,语法格式如下:

tuple_name[index]

实例

以下实例定义了元组,包含了数字和字符串两种类型的元素:

var mytuple = [10,"Noodles"]; // 创建元组
console.log(mytuple[0]) //10
console.log(mytuple[1]) //Noodles

元组运算

我们可以使用以下两个函数向元组添加新元素或者删除元素:

  • push() 向元组添加元素,添加在最后面。

  • pop() 从元组中移除元素(最后一个),并返回移除的元素。

var mytuple = [10,"Hello","World","typeScript"]; 
console.log("添加前元素个数:"+mytuple.length)    // 返回元组的大小
 
mytuple.push(12)                                    // 添加到元组中
console.log("添加后元素个数:"+mytuple.length) //5
console.log("删除前元素个数:"+mytuple.length) //5
console.log(mytuple.pop()+" 元素从元组中删除") // 删除并返回删除的元素
        
console.log("删除后元素个数:"+mytuple.length) //4

更新元组

元组是可变的,这意味着我们可以对元组进行更新操作:

var mytuple = [10, "Runoob", "Taobao", "Google"]; // 创建一个元组
console.log("元组的第一个元素为:" + mytuple[0]) 
 
// 更新元组元素
mytuple[0] = 121     
console.log("元组中的第一个元素更新为:"+ mytuple[0])

解构元组

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

var a =[10,"Noodles"] 
var [b,c] = a 
console.log( b )   //10 
console.log( c )   //Noodles

TypeScript 联合类型

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

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

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

Type1|Type2|Type3 

实例

声明一个联合类型:

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

联合类型数组

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

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 接口

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

TypeScript 接口定义如下:

interface interface_name { 
}

实例

以下实例中,我们定义了一个接口 IPerson,接着定义了一个变量 customer,它的类型是 IPerson。

customer 实现了接口 IPerson 的属性和方法。

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 的一部分。

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

var customer = {
    firstName: "Tom",
    lastName: "Hanks",
    sayHi: function () { return "Hi there"; }
};
console.log("Customer 对象 ");
console.log(customer.firstName);
console.log(customer.lastName);
console.log(customer.sayHi());
var employee = {
    firstName: "Jim",
    lastName: "Blakes",
    sayHi: function () { return "Hello!!!"; }
};
console.log("Employee  对象 ");
console.log(employee.firstName);
console.log(employee.lastName);

输出结果:

Customer 对象
Tom
Hanks
Hi there
Employee  对象
Jim
Blakes

联合类型和接口

以下实例演示了如何在接口中使用联合类型:

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());

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

// commandline 是字符串
var options = { 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: function () { return "**Hello World**"; } };
var fn = options.commandline;
console.log(fn());

接口和数组

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

设置元素为字符串类型:

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

如果使用了其他类型会报错:

interface namelist { 
   [index:number]:string 
} 
 
// 类型一致,正确
// var list2:namelist = ["Google","Runoob","Taobao"]
// 错误元素 1 不是 string 类型
var list2:namelist = ["John",1,"Bran"]

执行后报错如下,显示类型不一致:

test.ts:8:30 - error TS2322: Type 'number' is not assignable to type 'string'.

8 var list2:namelist = ["John",1,"Bran"]
                               ~

  test.ts:2:4
    2    [index:number]:string
         ~~~~~~~~~~~~~~~~~~~~~
    The expected type comes from this index signature.


Found 1 error.

接口继承

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

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

继承使用关键字 extends。

单接口继承语法格式:

Child_interface_name extends super_interface_name

多接口继承语法格式:

Child_interface_name extends super_interface1_name, super_interface2_name,…,super_interfaceN_name

继承的各个接口使用逗号 , 分隔。

单继承实例

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)

多继承实例

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 类定义方式如下:

class class_name { 
    // 类作用域
}

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

  • 字段 − 字段是类里面声明的变量。字段表示对象的有关数据。

  • 构造函数 − 类实例化时调用,可以为类的对象分配内存。

  • 方法 − 方法为对象要执行的操作。

实例

创建一个 Person 类:

class Person {
}

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

var Person = /** @class */ (function () {
    function Person() {
    }
    return Person;
}());

创建类的数据成员

以下实例我们声明了类 Car,包含字段为 engine,构造函数在类实例化后初始化字段 engine。

this 关键字表示当前类实例化的对象。注意构造函数的参数名与字段名相同,this.engine 表示类的字段。

此外我们也在类中定义了一个方法 disp()。

class Car { 
    // 字段 
    engine:string; 
 
    // 构造函数 
    constructor(engine:string) { 
        this.engine = engine 
    }  
 
    // 方法 
    disp():void { 
        console.log("发动机为 :   "+this.engine) 
    } 
}

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

var Car = /** @class */ (function () {
    // 构造函数 
    function Car(engine) {
        this.engine = engine;
    }
    // 方法 
    Car.prototype.disp = function () {
        console.log("发动机为 :   " + this.engine);
    };
    return Car;
}());

创建实例化对象

我们使用 new 关键字来实例化类的对象,语法格式如下:

var object_name = new class_name([ arguments ])

类实例化时会调用构造函数,例如:

var obj = new Car("Engine 1")

类中的字段属性和方法可以使用 . 号来访问:

// 访问属性
obj.field_name 

// 访问方法
obj.function_name()

完整实例

以下实例创建来一个 Car 类,然后通过关键字 new 来创建一个对象并访问属性和方法:

class Car { 
   // 字段
   engine:string; 
   
   // 构造函数
   constructor(engine:string) { 
      this.engine = engine 
   }  
   
   // 方法
   disp():void { 
      console.log("函数中显示发动机型号  :   "+this.engine) 
   } 
} 
 
// 创建一个对象
var obj = new Car("XXSY1")
 
// 访问字段
console.log("读取发动机型号 :  "+obj.engine)  
 
// 访问方法
obj.disp()

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

var Car = /** @class */ (function () {
    // 构造函数
    function Car(engine) {
        this.engine = engine;
    }
    // 方法
    Car.prototype.disp = function () {
        console.log("函数中显示发动机型号  :   " + this.engine);
    };
    return Car;
}());
// 创建一个对象
var obj = new Car("XXSY1");
// 访问字段
console.log("读取发动机型号 :  " + obj.engine);
// 访问方法
obj.disp();

类的继承

TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。

类继承使用关键字 extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。

TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。

语法格式如下:

class child_class_name extends parent_class_name

实例

类的继承:实例中创建了 Shape 类,Circle 类继承了 Shape 类,Circle 类可以直接使用 Area 属性:

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()

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

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Shape = /** @class */ (function () {
    function Shape(a) {
        this.Area = a;
    }
    return Shape;
}());
var Circle = /** @class */ (function (_super) {
    __extends(Circle, _super);
    function Circle() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Circle.prototype.disp = function () {
        console.log("圆的面积:  " + this.Area);
    };
    return Circle;
}(Shape));
var obj = new Circle(223);
obj.disp();

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

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)

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

var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var Root = /** @class */ (function () {
    function Root() {
    }
    return Root;
}());
var Child = /** @class */ (function (_super) {
    __extends(Child, _super);
    function Child() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return Child;
}(Root));
var Leaf = /** @class */ (function (_super) {
    __extends(Leaf, _super);
    function Leaf() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return Leaf;
}(Child)); // 多重继承,继承了 Child 和 Root 类
var obj = new Leaf();
obj.str = "hello";
console.log(obj.str);

继承类的方法重写

类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。

其中 super 关键字是对父类的直接引用,该关键字可以引用父类的属性和方法。

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

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

var obj = new StringPrinter() 
obj.doPrint()
 
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var PrinterClass = /** @class */ (function () {
    function PrinterClass() {
    }
    PrinterClass.prototype.doPrint = function () {
        console.log("父类的 doPrint() 方法。");
    };
    return PrinterClass;
}());
var StringPrinter = /** @class */ (function (_super) {
    __extends(StringPrinter, _super);
    function StringPrinter() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    StringPrinter.prototype.doPrint = function () {
        _super.prototype.doPrint.call(this); // 调用父类的函数
        console.log("子类的 doPrint()方法。");
    };
    return StringPrinter;
}(PrinterClass));
var obj = new StringPrinter();
obj.doPrint();

static 关键字

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

class StaticMem {  
   static num:number; 
   
   static disp():void { 
      console.log("num 值为 "+ StaticMem.num) 
   } 
} 
 
StaticMem.num = 12     // 初始化静态变量
StaticMem.disp()       // 调用静态方法

instanceof 运算符

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

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 则会编译错误。

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 接口:

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 )

TypeScript 对象

对象是包含一组键值对的实例。 值可以是标量、函数、数组、对象等,如下实例:

var object_name = { 
    key1: "value1", // 标量
    key2: "value",  
    key3: function() {
        // 函数
    }, 
    key4:["content1", "content2"] //集合
}

以上对象包含了标量,函数,集合(数组或元组)。

对象实例

var sites = { 
   site1:"Runoob", 
   site2:"Google" 
}; 
// 访问对象的值
console.log(sites.site1) 
console.log(sites.site2)

TypeScript 类型模板

假如我们在 JavaScript 定义了一个对象:        

var sites = { 
   site1:"Runoob", 
   site2:"Google" 
};

这时如果我们想在对象中添加方法,可以做以下修改:

sites.sayHello = function(){ return "hello";}

如果在 TypeScript 中使用以上方式则会出现编译错误,因为Typescript 中的对象必须是特定类型的实例。

var sites = {
    site1: "Runoob",
    site2: "Google",
    sayHello: function () { } // 类型模板
};
sites.sayHello = function () {
    console.log("hello " + sites.site1);
};
sites.sayHello();

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

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)的一种形式。

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

可以这样表述:

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

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

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})

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值