TypeScript学习笔记

TypeScript学习笔记

TypeScript 是JavaScript的超集且支持ES6标准,由微软开发。

TypeScript是设计用来开发大型应用的,可以编译成js并运行在浏览器上。


语言特性

ts扩展的内容

TypeScript是js的扩展,扩展的内容如下:

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

以及从ECMA2015一直了如下内容:

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

ts和js的区别

ts是js的超集,它扩展了js的语法。

ts通过类型注解提供编译时的静态类型检查。

ts可以处理已有的js代码并只对其中的ts代码进行编译。

实例

1
2
const str : string = "this is a string."
console.log(str);

安装

npm安装

1
2
3
4
5
# 使用npm全局安装ts
cnpm install -g typescript

# 查看是否安装成功
tsc -v

编译

将ts编译为js代码:

1
2
3
# ts文件扩展名为.ts
tsc demo.ts
tsc demo1.ts demo2.ts demo3.ts

运行

ts已经被编译成了js文件,使用node命令运行js。

1
node demo.js

基本语法

TypeScript程序由模块、函数、变量、语句和表达式以及注释组成。

编译参数

tsc常用编译参数:

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
# 显示帮助信息
--help

# 载入扩展模块
--module

# 设置ECMA版本
--target

# 额外生成一个.d.ts扩展名的文件。
tsc ts-hw.ts --declaration

# 删除文件的注释
--removeComments

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

# 生成一个 sourcemap (.map) 文件。sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。
--sourcemap

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

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

保留关键字

  • 保留关键字
1
2
3
4
5
6
7
8
9
10
11
12
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
new try yield const
continue do
  • 空白和换行

ts会忽略空格、tab和换行。

  • ts区别大小写

  • ts中分号可选

建议使用分号。

  • 注释

建议每段代码都写注释以提高程序可读性。

编译器会忽略注释。

ts支持的注释类型:

1
2
3
4
5
// 单行注释

/*
多行注释。
*/

面向对象

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

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

  • 类:类是一个模板,描述一类对象的状态和行为。
  • 对象:对象是类的实例,有状态和行为。
  • 方法:方法是类的操作的实现步骤。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 定义一个类
class Person {
// 定义一个name方法
name():void {
console.log("wangwei")
}
}

// 创建Person这个类的实例mason
var mason = new Person();

// 调用mason这个实例的name方法
mason.name();

现在将这个ts编译为js,编译之后的js代码为:

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

基本数据类型

  • any关键字声明任意类型。

TypeScript针对类型不明确时提供了any类型。

三种用途:

  1. 变量的值会动态改变。
1
2
3
4
5
6
7
let x: any = 1;

x = 'abc';

x = true;

x = 2;
  1. 改写原有代码的时候,使用any类型可以允许在编译的时候可选择的包含或者移除类型检查。
1
2
3
4
5
let x: any = 4;

x.ifItExists();

x.toFixed();
  1. 定义存储各种类型数据的数组的时候使用any。
1
2
let arr: any[] = [1,'a string',true];
arr[0] = 'new string';
  • number关键字声明数字类型,采用双精度64位浮点数,可以用来表示整数和分数。
1
2
3
4
5
6
7
8
9
10
11
// 声明并赋值一个二进制数
let num: number = 0b10101;

// 声明并赋值一个八进制数
let num: number = 0o712;

// 声明并赋值一个十进制数
let num: number = 9;

// 声明并赋值一个十六进制数
let num: number = 0xf100a;
  • string关键字声明字符串类型,使用单引号或者双引号表示字符串,反引号`定义多行文本和内嵌表达式。
1
2
3
4
let name: string = 'mason';
let age: number = 18;
let gender: string = 'male';
let words: string = `我的名字叫${name},今年${age}岁,性别${gender}。`;
  • boolean关键字声明布尔型,两个值为truefalse
1
let fake: boolean = false;
  • number[]来声明数组类型。
1
2
3
4
5
// 在元素类型后面加上[]即可声明数组
let arr: number[] = [1,2,3];

// 或者使用数组泛型
let arr: Array<number> = [1,2,3];
  • 元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
1
2
3
4
5
6
7
8
9
10
11
// 声明元组,元组各个位置上的数据类型要相符,下面尝试赋值
let x: [string, number];

// 赋值正常
x = ['asd',1];

// 赋值报错
x = [1,'add'];

// 输出asd
console.log(x[0]);
  • enum关键字,枚举类型用于定义数值集合。
1
2
3
enum Color {Red, Green, Blue};
let c: Color = Color.Blue;
console.log(c);
  • void关键字用于标识方法没有返回值。
1
2
3
function func(): void {
console.log('done');
}
  • null关键字标识对象值缺失。

js中的null表示什么都没有。
null是一个只有一个值的特殊类型,表示一个空对象引用。
typeof null 得到 object

  • undefined关键字用于初始化变量为一个未定义的值。

js中的undefined是一个没有设置值的变量。

  • never是其他类型的子类型,代表不会出现的值。

声明为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) {}
}

JavaScript是弱类型语言故而没有整数类型,TypeScript是强类型语言但是只能用number关键字声明数字并且不区分整数和浮点数故而也没有整数类型。

可以使用|来支持多种类型

1
let x: number | null | undefined;

tips:

如果某个属性的值是计算出来的,则它后面的一个变量必须初始化,否则编译报错。

变量声明

变量用于引用内存地址。

变量可以看做是存储数据的容器。

ts变量命名规则:变量名由数字、字母和 _ 以及 $ 构成,且不能以数字开头。

变量使用前必须声明。

各种声明方式:

1
2
3
4
5
6
7
8
9
10
11
// 声明变量类型并初始化
var [var_name]: [data_type] = [value];

// 声明变量但不初始化,值默认为undefined
var [var_name]: [data_type];

// 声明变量并初始化,但不设置类型,此时变量可以是任意类型
var [var_name] = [value];

// 声明变量,不设置类型和初始化,类型可以是任意类型,值默认为undefined
var [var_name];

实例:

1
2
3
4
5
6
7
8
var uname:string = "mason";
var score1:number = 90;
var score2:number = 41.5
var sum = score1 + score2
console.log("名字: "+uname)
console.log("第一个科目成绩: "+score1)
console.log("第二个科目成绩: "+score2)
console.log("总成绩: "+sum)

变量名不能使用 name ,会与 windows.name 冲突。

编译为js:

1
2
3
4
5
6
7
8
var uname = "mason";
var score1 = 90;
var score2 = 41.5;
var sum = score1 + score2;
console.log("名字: " + uname);
console.log("第一个科目成绩: " + score1);
console.log("第二个科目成绩: " + score2);
console.log("总成绩: " + sum);

ts是强类型语言,声明类型和字面量类型不同则编译错误:

1
2
// 声明为number类型却复制为string类型,编译错误
var num: number = "this is a string.";

类型断言

类型断言(Type Assertion)可以用于手动指定一个值的类型,也就是将一个变量改变为另一个类型。

1
2
3
4
5
// 方法一
<类型>值

// 方法二,在tsx中必须使用该方法
as 类型

实例:

1
2
3
4
5
// 声明变量为string类型
var str = '1';

// 使用方法一将其变为number类型
var str2: number = <number> <any> str;

关于断言:

  1. 当A类型是B类型的子集或者A类型是B类型的父集,A类型都能被成功断言为A类型,这个是为了安全考虑,若强行断言则可以使用any
  2. 类型断言不是类型转换,类型转换是在程序运行时完成的,而类型断言则是纯粹的编译时语法。

类型推断

当没有给出类型的时候,tsc会利用类型推断来推断类型。

如果缺乏声明而导致不能推断出类型,则默认其类型为 any

1
2
3
4
5
6
// 注意这里不是js声明,这里是ts声明变量,却没有声明类型,类型推断为number
var num = 2;

// 这里num已经被推断为number却赋值了string类型,故而编译失败
num = 'this is a string.';
console.log(num);

变量作用域

ts中的变量作用域:

  1. 全局作用域:全局变量定义在程序结构的外部,在任何位置都可以调用。
  2. 类作用域:也称之为字段,类变量声明在一个类里面且在类的方法外面。可以使用类的对象访问。类变量可以使静态的,可以通过类名直接访问。
  3. 局部作用域:局部变量,生命在一个代码块中,作用域就在这个代码块。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 全局变量
var global_var = 1;

class Numbers {
// 实例变量
template_var = 13;

// 静态变量
static static_var = 14;

varNum(): void {
// 局部变量
var scoped_var = 123;
}
}

这是上面的四种变量对应的访问方式:

1
2
3
4
5
6
7
8
9
10
11
// 全局变量
console.log(global_var);

// 实例变量,需要先new一个实例
var obj = new Numbers();
console.log(obj.template_var);

// 静态变量
console.log(Numbers.static_var);

// 局部变量外部无法访问,只能在其所在的代码块访问

更多参考:https://ts.xcatliu.com/basics/type-assertion.html

运算符

ts有以下几种运算符:

  1. 算术运算符
  2. 逻辑运算符
  3. 关系运算符
  4. 按位运算符
  5. 赋值运算符
  6. 三元/条件运算符
  7. 字符串运算符
  8. 类型运算符

算数运算符

+ , - , * , / , % , ++ , -- ,其中 ++ , -- 还有写在值之前和之后之分。

比如 ++ ,写在值之前是先自增再用,写在值之后是先用再自增。

关系运算符

关系运算法计算表达式是 true 还是 false

关系运算符有这些: == , != , > , < , >= , <=

逻辑运算符

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

逻辑运算符有这些: && , || , !

其中 &&|| 可以短路: && 左边为 true 则返回右边的, || 之前的变量或值为 true 则返回 && 之前的变量或值。

位运算符

位运算符是对变量按位进行二进制操作(有一元操作符和二元操作符)。

位运算符有这些:

  • &:按位与处理两个长度相同的二进制数。
  • |:按位或处理两个长度相同的二进制数。
  • ~:取反,取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。
  • ^:按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为 1,否则该位为 0。
  • <<:把 << 左边的运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0。
  • >>:把 >> 左边的运算数的各二进位全部右移若干位,>> 右边的数指定移动的位数。
  • >>>:无符号右移,与有符号右移位类似,除了左边一律使用0 补位。
1
2
3

alert: 这里b是3,按位取反后为什么是-4?
(~b) => -4

赋值运算符

赋值运算符有: = , += , -= , *= , /=

三元运算符

语法: condition ? expression1 : expression2

三元运算符 ? 有三个操作数,第一个操作数为 true 则返回第二个操作数,为 false 则返回第三个操作数。

实例:

1
2
var num: number = -1;
var result = num >= 0 ? "大于0" : "小于0";

类型运算符

  • typeof:一元运算符,返回数据类型。
1
2
3
4
var num: number = 12;
console.log(typeof num);

// 结果:number
  • instanceof:判断对象是否是指定类型。

其他运算符

  • 符号运算符:用于取负值。

  • 字符串运算符(链接运算符):用于拼接两个字符串。

条件语句

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

if语句

if语句由布尔表达式和多个语句组成。

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// type1
if (expression) {
// statement
}

// type2
if (expression) {
// statement1
} else {
// statement2
}

// type3
if (expression) {
// statement1
} else if () {
// statement2
} else {
// statement3
}

switch…case语句

switch...case 语句允许测试一个变量等于多个值时的情况,每一个值都是一个case,被测变量以此检查case。

switch…case语句规则:

  • expression是一个常量表达式(整形或者枚举型)。
  • case可以与多个,后面跟一个值和冒号。
  • expression和exp必须要是相同的数据类型。
  • expression和exp相等时执行case后面的语句,直到遇到break;跳出switch。
  • 如果语句不包含break则顺序往下面的case执行直到遇到break。
  • switch可以有多个default,放置在switch最后面。

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
switch(expression) {
case exp1: {
// statement
break; // 可选
}
case exp2: {
// statement
break; // 可选
}
...
...
...
default: {
// statement
}
}

循环

for循环

1
2
3
for (init; condition; inc) {
// statement
}

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

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

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

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

实例:计算5的阶乘5!

1
2
3
4
5
6
var result: number = 1;
for (let count: number = 5; count > 0; count--) {
result = result * count;
}
console.log(result);
// 120

for…in循环

for…in循环用于一组值的集合或列表进行迭代输出。

1
2
3
for (var val in list) {
// statement
}

val 为string或者any。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var num: any;
var str: any = "a b c";

for (num in str) {
console.log(str[num]);
}

/*
a

b

c
*/

for…of, forEach, every, some

  • for...of是es6中引入的代替for...inforEach(),用于创建一个循环来迭代可迭代的对象,支持新的迭代协议。

可以使用for…of遍历数组、字符串、映射(maps)、集合(sets)等等可迭代的数据结构。

实例:

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

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

forEach , everysome 是js中的循环语法,ts也支持这几种循环。

forEach

1
2
3
4
5
6
let list = [1,2,3];
list.forEach((val, idx, array) => {
// val: 当前值
// idx:当前index
// array: Array
});

every

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
while (condition) {
// statement
}

do…while

do...while 中的statement代码至少被执行一次。

1
2
3
do {
// statement
} while ();

break

用法:

  1. 在循环内使用的时候会终止并跳出当前循环。
  2. switch…case中使用跳出switch。

语法:

1
break;

continue

continue跳过当前循环开始下一个循环。

语法:

1
continue;

无限循环

for和while都可以创建无限循环(死循环)。

1
2
3
4
5
6
7
for (;;) {
// statement
}

while (true) {
//statement
}

函数

函数是一组ts语句。

函数声明包括函数的名称、返回值类型和参数。

函数的定义

1
2
3
4
5
6
7
8
9
// 一个名为func的函数
function func() {
// statement
}

// 匿名函数
function () {
// statement
}

调用函数

调用上面的 func 函数。

1
func();

函数返回值

有时我们希望函数能返回一个值用于调用它的地方,通过return语句可以返回一个值并停止函数的执行。

1
2
3
4
function func(): string {
// statement
return str;
}

一个函数只能有一个return,返回值类型要和函数定义的返回值类型一样。

带参数的函数

1
2
3
function func(param1: number, param2: string,param3: boolean) {
//statement
}

可选参数和默认参数

  • 可选参数

使用问号 ? 表示可选参数,且只能放在后面。

1
2
3
4
// 这里第三个参数是可选的
function func(param1: number, param2: string,param3?: boolean) {
//statement
}
  • 默认参数
1
2
3
4
这里给第三个参数默认值`true`
function func(param1: number, param2: string,param3: boolean = true) {
//statement
}

剩余参数

用于不知道向函数传多少个参数的时候。

使用 ...restOfName 表示剩余参数,是一个数组。

1
2
3
4
5
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

匿名函数

匿名函数就是没有函数名的函数。

匿名函数用的时候动态声明。

也可以将匿名函数赋值给变量,这种就叫做函数表达式。

1
let func = function () { //statement }

匿名函数立即执行

1
2
3
(function () {
// statement
})();

构造函数

ts也支持js的 new Function() 来定义函数。

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

实例:

1
2
3
var myFunction = new Function("a", "b", "return a * b"); 
var x = myFunction(4, 3);
console.log(x);

递归函数

递归函数就是在函数内部调用自身。

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

lambda函数

lambda 函数就是箭头函数。

单个参数 () 可选,单行函数体则 {} 也是可选的,如果没有参数则要保留一堆括号 ()

语法:

1
2
3
4
5
6
7
// 单行
( [param1, parma2,…param n] ) => statement;

// 多行
( [param1, parma2,…param n] )=> {
statement;
}

实例:

1
var foo = (x: number) => x++;

上面的代码等同于:

1
2
3
var foo = function(x) {
return x++;
};

函数重载

重载就是调用的函数相同但是参数不同,返回值类型可以不同。

每个重载的方法都要有独一无二的参数类型列表。

重载的例子:

1
2
3
4
5
6
7
8
9
10
11
// 参数类型不同
function func(`str`): void;
function func(1): void;

// 参数数量不同
function func(n1:number): void;
function func(n1: number, n2: number): void;

// 参数类型顺序不同
function func(num: number, str: string): void;
function func(str: string, num: number): void;

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

参数数量不同,要将不同的参数设置为可选。

实例:参数数量和类型不同

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

Number对象

ts也支持Number对象,Number对象是原始数据类型的包装对象。

1
var num = new Number(1);

Number对象属性

  1. MAX_VALUE:可表示的最大的数字。
  2. MIN_VALUE:可表示的最小的数字。
  3. NaN:not a number。
  4. POSITIVE_INFINITY:正无穷。
  5. NEGATIVE_INFINITY:负无穷。
  6. prototype:Number 对象的静态属性。使您有能力向对象添加属性和方法。
  7. constructor:返回对创建此对象的Number函数的引用。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log("最大值为: " + Number.MAX_VALUE); 
console.log("最小值为: " + Number.MIN_VALUE);
console.log("负无穷大: " + Number.NEGATIVE_INFINITY);
console.log("正无穷大:" + Number.POSITIVE_INFINITY);

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)
console.log("员工姓名: "+emp.name)
console.log("员工邮箱: "+emp.email)

输出:

1
2
3
4

员工号: 123
员工姓名: admin
员工邮箱: admin@runoob.com

Number对象方法

  1. toExponential():将数字转化为指数计数法。
  2. toFixed():将数字指定小数点位数并转化为字符串。
  3. toLocaleString():把数字转换为字符串,使用本地数字格式顺序。
  4. toPrecision():把数字格式化为指定的长度。
  5. toString():把数字转换为字符串,使用指定的基数。数字的基数是2 ~ 36之间的整数。若省略该参数,则使用基数10。
  6. valueOf():返回一个Number对象的原始数字值。

String对象

String对象用于处理字符串。

1
2
var str = new String('this is a string.');
var str = 'this is a string.';

String对象属性

  1. constructor:对创建该对象的构造函数的引用。
  2. length:返回字符串的长度。
  3. prototype:可以向对象添加属性和方法。

String对象方法

  1. charAt():返回在指定位置的字符。
  2. charCodeAt():返回在指定的位置的字符的Unicode编码。
  3. concat():连接两个或更多字符串,并返回新的字符串。
  4. indexOf():返回某个指定的字符串值在字符串中首次出现的位置。
  5. lastIndexOf():从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。
  6. localeCompare():用本地特定的顺序来比较两个字符串。
  7. match():查找找到一个或多个正则表达式的匹配。
  8. replace():替换与正则表达式匹配的子串。
  9. search():检索与正则表达式相匹配的值。
  10. slice():提取字符串的片断,并在新的字符串中返回被提取的部分。
  11. split():把字符串分割为子字符串数组。
  12. substr():从起始索引号提取字符串中指定数目的字符。
  13. substring():提取字符串中两个指定的索引号之间的字符。
  14. toLocaleLowerCase():根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射。
  15. toLocaleUpperCase():据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射。
  16. toLowerCase():把字符串转换为小写。
  17. toString():返回字符串。
  18. toUpperCase():把字符串转换为大写。
  19. valueOf():返回指定字符串对象的原始值。

Array对象

ts如何声明数组:

1
2
3
4
5
// 声明一个变量为site的字符串类型的数组
var sites: string[];

// 初始化
sites = ['twitter','google','amazon'];

或者声明并初始化:

1
var sites: string[] = ['baidu','google','amazon'];

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

访问数组:

1
2
3
4
var sites: string[];
sites = ['baidu','google','amazon'];

console.log(sites[0]);

使用Array对象创建数组

Array创建对象的时候可以接受两种值。

  1. 数组的长度。
  2. 初始化数组的元素。
1
var sites: string[] = new Array(3);

或者:

1
var sites: string[] = new Array('google','baidu','amazon');

数组结构

也可以将数组元素赋值给变量:

1
2
var arr: number[] = [1,2];
var [x,y] = arr;

数组迭代

1
2
3
4
5
6
var nums:number[] = [1,2,3,4];
var i:any;

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

多维数组

可以将一个数组作为另一个数组的元素,这就是多维数组。

二维数组定义:

1
var arr: number[][] = [[1,2,3],[4,5,6]];

数组在函数中的使用

  • 作为参数传递到函数进行操作。
  • 作为函数的返回值。
1
2
3
4
5
function returnArray():string[] {
return new Array('google','facebook','amazon');
}

// 这里会返回一个数组

数组方法

  1. concat():连接多个数组。
1
2
3
4
var alpha = ['a','b','c'];
var nums = [1,2,3];

var alphaConcatNums = alpha.concat(nums);
  1. every():检测数组元素是否都符合条件。
1
2
3
4
5
6
7
8
9
// 声明并初始化待检测数组
var arr = [1,2,3,4,5,6];

// 定义检测函数
function isBigEnough(element, index, array) {
return (element >= 3);
}

console.log(arr.every(isBigEnough));
  1. filter():检测数值元素并返回符合条件的数组。
1
2
3
4
5
6
7
8
9
// 声明并初始化待检测数组
var arr = [1,2,3,4,5,6];

// 定义检测函数
function isBigEnough(element, index, array) {
return (element >= 3);
}

console.log(arr.filter(isBigEnough));
  1. forEach():为数组的每一个元素都执行一次回调函数。
1
2
3
4
let nums = [1,2,3];
nums.forEach(function (element) {
console.log(element);
});
  1. indexOf():返回一个元素所在的索引位置,找不到则返回-1.
1
console.log([1,2,3,4].indexOf(2));
  1. join():将数组的所有元素都放入一个字符串。
1
2
3
4
5
6
7
8
9
10
var arr = ['i','have','a','pen'];

console.log(arr.join());
// i,have,a,pen

console.log(arr.join(', '));
// i, have, a, pen

console.log(arr.join('+'));
// i+have+a+pen
  1. lastIndexOf():返回指定字符串最后出现的位置(会从最后面向前搜索)。
1
2
3
var index = [12, 5, 8, 130, 44].lastIndexOf(8); 
console.log("index is : " + index );
// 2
  1. map():用指定函数梳理数组中每一个元素并返回处理后的数组。
1
2
3
var numbers = [1, 4, 9]; 
var roots = numbers.map(Math.sqrt);
console.log("roots is : " + roots ); // 1,2,3
  1. pop():删除数组的最后一个元素并返回删除的元素。
1
2
3
4
var numbers = [1, 4, 9]; 

var element = numbers.pop();
console.log("element is : " + element ); // 9
  1. push():向数组添加元素并返回新数组的长度。
1
2
3
4
5
var numbers = [1, 4, 9]; 

var length = numbers.push(16);
console.log(length);
// 4
  1. reduce():从左到右将数组元素计算为一个值。
1
2
3
4
5
6
7
8
var arr = [1,2,3,4];

var total = arr.reduce(function (a,b) {
return a + b;
});

console.log(total);
// 10
  1. reduceRight():从右到左将数组元素计算为一个值。

  2. reverse():反转数组的元素顺序。

1
2
3
4
5
var arr = [1,2,3,4];

var reverseArr = arr.reverse();
console.log(reverseArr);
// [4,3,2,1]
  1. shift():删除并返回数组的第一个元素。
1
2
3
4
var arr = [1,2,3,4].shift();

console.log(arr);
// 1
  1. 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
  1. some():检测数组中是否存在符合条件的元素并返回布尔值。
1
2
3
4
5
6
7
8
9
10
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
  1. 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
  1. 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
  1. 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
  1. 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

Map对象

Map对象是es6引入的,用于保存键值对并且能记住键的原始插入顺序。

创建Map

1
2
3
4
5
6
7
8
// 声明
let myMap = new Map();

// 声明并用数组的方式传入键值对
let myMap = new Map([
["key1", "value1"],
["key2", "value2"]
]);

Map属性与方法

  • map.clear():删除map对象的所有键值对。
  • map.set():设置键值对,返回该对象。
  • 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
let nameSiteMapping = new Map();

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

console.log(nameSiteMapping.get("Runoob")); //40

console.log(nameSiteMapping.has("Taobao")); //true
console.log(nameSiteMapping.has("Zhihu")); //false

console.log(nameSiteMapping.size); //3

console.log(nameSiteMapping.delete("Runoob")); // true
console.log(nameSiteMapping);

nameSiteMapping.clear(); //清除 Map
console.log(nameSiteMapping);

使用es6编译:

1
tsc --target es6 test.ts

运行结果:

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

迭代Map

使用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
Google
Runoob
Taobao
1
2
3
Google 1
Runoob 2
Taobao 3
Google 1
Runoob 2

元组

数组中存储的元素类型都是一样的,如果要在数组中存储不同类型的元素则用元组。

定义元组

1
var mytuple = [val1,val2,val3...];

实例:

1
2
3
4
5
6
7
8
var mytuple = [1,'asd',false];

或者

var mytuple = [];
mytuple[0] = 1;
mytuple[1] = 'asd';
mytuple[2] = false;

访问元组

1
mytuple[index];

元组的运算

  • push():向元组最后面添加元素。
  • pop():删除元组最后面一个元素并返回被删除的元素。

更新元组

可以改变元组元素。

结构元组

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

联合类型

可以通过使用管道符 | 将变量设置为多种类型。

1
type1 | type2 | type3

实例:

1
2
3
var val: string | number;
val = 12;
val = 'string.';

将联合类型作为函数的参数使用:

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

接口

接口的定义

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

ts接口的定义:

1
2
interface interface_name {
}

实例:

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
27
28
// 定义了接口IPerson
interface IPerson {
firstName:string,
lastName:string,
sayHi: ()=>string
}

// 定义了类型为IPerson的变量customer
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)

接口不能编译为js。

联合类型和接口

在接口中使用联合类型:

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

接口和数组

接口中可以将数组的index和value设置为不同类型。

接口的继承

接口的继承就是一个接口可以继承多个接口。
使用关键字 extends

使用:

1
2
3
interface_A extends interface_B

interface_A extends interface_B, interface_C, interface_D

实例:单继承

1
2
3
4
5
6
7
interface Person { 
age:number
}

interface Musician extends Person {
instrument:string
}

实例:多继承

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

interface IParent2 {
v2:number
}

interface Child extends IParent1, IParent2 { }

类的定义

ts是面向对象的js。

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

ts支持面向对象的所有特性:类、接口等。

ts类的定义:

1
2
3
4
5
class class_name {
// 字段:是类里面声明的变量。字段表示对象的有关数据。
// 构造函数:类实例化时调用,可以为类的对象分配内存。
// 方法:方法为对象要执行的操作。
}

实例:

1
2
class Person {
}

创建类的数据成员

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

实例化

使用 new 来实例化类的对象。

1
2
3
4
5
var object_name = new class_name([ arguments ]);
var obj = new Car("Engine 1");

obj.var_name;
obj.function_name();

实例:

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

类的继承

使用关键字 extends 继承父类,只能继承一个父类。

1
class class_A extends class_B

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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()

// 圆的面积: 223

ts中一个类只能继承一个父类,但是支持多重继承。

1
2
3
4
5
6
7
8
9
10
11
12
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)

// hello

继承类的方法重写

子类继承了父类以后,子类可以对父类的方法重写。

super 关键字是对父类的引用,可以使用 super 引用父类的属性和方法。

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

class StringPrinter extends PrinterClass {
// 重写doPrint
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运算符

用于判断对象是否是指定的类型。

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

访问控制修饰符

ts中可以使用访问控制修饰符来保护对类、变量、方法和构造方法的访问。

  • public:默认值,共有,在任何地方都可以被访问。
  • protected:受保护的,只可以被自己和子类和父类访问。
  • private:私有的,只能被其定义所在的类访问。
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 字段作为类的属性使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

对象

对象的定义

对象是键值对的集合。

1
2
3
4
5
6
7
8
var object_name = { 
key1: "value1", // 标量
key2: "value",
key3: function() {
// 函数
},
key4:["content1", "content2"] //集合
}
1
2
3
4
5
6
7
var sites = { 
site1:"Runoob",
site2:"Google"
};
// 访问对象的值
console.log(sites.site1)
console.log(sites.site2)

类型模板

js对象添加方法:

1
2
3
4
5
6
7
8
var sites = {
site1: "Runoob",
site2: "Google"
};

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

ts中的对象必须是特定类型的实例:

1
2
3
4
5
6
7
8
9
var sites = {
site1: "Runoob",
site2: "Google",
sayHello: function () { } // 类型模板
};
sites.sayHello = function () {
console.log("hello " + sites.site1);
};
sites.sayHello();

鸭子类型

鸭子类型是动态类型的一种风格,是多态的一种形式。

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

命名空间

命名空间是用来解决命名重名的问题。
命名空间定义了标识符的可见范围。
ts中使用 namespace 来定义命名空间。

定义命名空间:

1
2
3
4
namespace SomeNameSpaceName { 
export interface ISomeInterfaceName { }
export class SomeClassName { }
}

使用 export 关键字让外部可以调用 SomeNameSpaceName 中的类和接口。

在另一个命名空间调用:

1
SomeNameSpaceName.SomeClassName;

如果一个命名空间在一个单独的ts文件中,使用 /// 引用它:

1
/// <reference path = "SomeFileName.ts" />

嵌套命名空间

可以将一个命名空间定义在另一个命名空间里面, 成员的访问使用点号。

模块

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

声明文件

声明

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

实例:ts获取id
在js中:

1
2
3
4
// js中使用jquery获取id
$('#foo');
// 或
jQuery('#foo');

在ts中:

1
2
3
// 用declare关键字定义它的类型
declare var jQuery: (selector: string) => any;
jQuery('#foo');

上面代码的编译结果是:

1
jQuery('#foo');

声明文件

声明文件以 .d.ts 结尾。

声明文件或者模块:

1
2
declare module Module_Name {
}

ts引入声明文件:

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

实例:
这是一个第三方库文件:CalcThirdPartyJsLib.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();

在ts中引用上面的文件,在声明文件中要这样写:

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

把声明文件加入到ts中:

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

然后编译ts文件。

然后在html中引入编译后的文件以及第三方库文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>title</title>
<script src="CalcThirdPartyJsLib.js"></script>
<script src="CalcTest.js"></script>
</head>

<body>

</body>

</html>

总结

接口、类、对象、命名空间、模块以及声明文件需要再配合其他资料理解一下。

参考

  1. https://www.runoob.com/typescript/ts-tutorial.html
  2. https://www.tslang.cn/docs/handbook/basic-types.html
  3. https://ts.xcatliu.com/

评论