TypeScript学习笔记

TypeScript是javaScript的超集,有一部分的语法与ES6相似,且更加趋近于面向对象编程,是现如今前端语言发展的主流方向

安装

使用npm installl -g typescript进行安装

安装之后可以使用tsc -v查看解析器的版本号同时验证安装

tsc的作用就是将ts转换为浏览器、nodejs可识别的js代码

运行

首先编写ts文件,编写后在终端运行tsc ***.ts的命令,可以将ts文件编译为一个node或浏览器可识别的js文件,然后运行即可

在node中运行ts会明显感觉到过于复杂,因此可以使用ts-node工具

安装方式: npm install -g ts-node

使用方式: ts-node ts文件名

生成配置: tsc --init

自动编译: 配置之后修改tsconfig文件实现自动编译 在vscode中点击终端=>点击运行任务 =>点击tsc监视器

数据类型

TypeScript规定: 在声明变量时必须指定变量类型

声明方式:

  • let 变量名 : 变量类型;
  • let age: number;
  • let myName: string;

TypeScript兼容所有JavaScript的数据类型: string, number, boolean, undefined, null, Symbol

undefined与null

javascript中undefined与null都是变量的值,undefined代表一个没有被初始化的变量,null指向一个空的对象

Typescript中undefined与null都是一种数据类型,undefined类型的变量,只能存储undefined的值,null类型的变量,只能存储null的值

1
2
3
4
5
6
7
let a1: undefined = undefined; //√
a1 = 10; //×
//undefined类型的变量只能存储undefined

let a2: null = null; // √
a2 = "aaa" // ×
//null类型的变量只能存储null

undefined与null类型的变量可以作为其他类型变量的值

例如:

1
2
let a2: null = null;
let a3: string = a2;

联合类型

声明方式:let 变量名: 数据类型1 | 数据类型2

变量取值时可以是两种类型中的任意一种

1
2
3
4
let res: string | number;
res = "aaaa"; //√
res = 123; //√
res = false; //×

any类型

any类型代表可以用来存储任意类型的数据,定义为any的变量可以被赋予任意类型的值

类型断言

类型断言等同于其他语言中的类型转换,它 不会对 运行时产生影响,仅仅只是在编译阶段起作用

类型断言有两种方式,其一是尖括号法:

1
2
let someValue: any = "this is a string";
let someLength: number = (<string>someValue).length

将someValue从any类型强制转换为了string类型

断言的第二种方式是as方式,也是做常用的断言方式 :

1
2
let someValue: any = "this is a string";
let someLength: number = (someValue as string).length

写法虽然有些不同,但是用法一致,代表的意义也是一样的,如果更加熟悉后端语言开发,那么肯定会对第一种写法情有独钟,因为它更加接近java\c++的类型转换写法,因此两种写法没有谁好谁坏,仅凭个人喜好

注意:当我们在Typescript中使用JSX时,只允许使用as语法!!!

函数

ES6定义

1
2
3
4
5
6
7
function a() {
console.log('function')
}

var b = function() {
console.log('function')
}

这是ES5中定义函数的两种方式

TS定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//限定返回值的定义方式
function run(): string {
return 'run';
}

//无返回值的定义方法
function run2(): void {
console.log('aaa');
}

//匿名函数方法
var fun2 = function(): string {
return 'fun2';
}

//定义方法传参
function getInfo(name: string, age: number): string {
return `${name}---${age}`;
}

console.log(getInfo('ryz', 20)) // ryz---20

可选参数

es5中实参与形参可以不一样,但是ts中必须一致,如果想要不一样,必须进行配置

使用?:的方式定义形参,代表为可选参数,可传可不传

可选参数必须写在形参的最后位置

1
2
3
4
5
6
7
8
9
10
function getInfo(name: string, age?: number):string {
if(age) {
return `${name}---${age}`
} else {
return `${name}---年龄保密`
}
}

console.log(getInfo('aaaa', 12)) // aaaa---12
console.log(getInfo('bbb')) // bbb---年龄保密

默认参数

可以使用age:string = 20的方式定义默认参数

剩余参数

可以使用扩展运算符...对剩余参数进行遍历

1
2
3
4
5
6
7
function sum(...result:number[]):number {
var sum = 0;
for(let i = 0; i < result.length; i++) {
sum+=result[i];
}
return sum;
}

函数重载

在java中函数重载意味着同名的函数可以通过传入不同的参数达到实现不同目的的效果

在ts中的函数重载与java中不一样,ts中的重载仅仅是为了类型检验,限制出入类型

1
2
3
4
5
6
7
8
9
function css(config:string):string;
function css(config:string, value: number):string;
function css(config:any, value?: number):any {
if(value) {
return `${config}---${value}`
} else {
return `${config}`
}
}

定义

typescript的类的定义方式与es6一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//es5定义
function Person(name) {
this.name = name;
this.run = function() {
cnosole.log(this.name)
}
}

//ts定义
class Person {
name: string;
constructor(name: string) {//构造函数
this.name = name
};
run():void {
console.log(this.name)
}
}

var p = new Person("rrr")
p.run() //rrr

继承

可以使用原型链的方式进行继承,也可以使用extends关键字继承

修饰符

publicprivateprotected用法与java中的修饰符用法一致

静态方法、静态属性

ES5中的静态方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
//静态方法
Person.run2 = function(){}
//实例方法
function Person() {
this.run1 = function(){}
}

//调用实例方法
let p = new Person()
p.run1();

//调用静态方法
Person.run2()

Ts中的静态方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
public name:string
constructor(name:string) {
thi8s.name = name;
}
run() {
console.log(`${this.name}在运动`)
}
//定义静态方法
static print() {
console.log("print方法")
}
}

//调用静态方法
Person.print()

静态方法可以直接使用类名进行调用,在静态方法中不允许调用非静态属性,只能调用静态属性

静态属性的定义与调用方式与静态方法完全一致

Abstruct

多态: 在父类中定义一个方法不去实现,在继承的子类中完成实现,这就是多态

abstruct关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中进行实现,这也是多态的一种实现方法

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
29
30
31
32
33
34
35
36
//定义抽象类
abstract class Animal {
name:string;
constructor(name:string) {
this.name = name
}
//定义抽象方法
abstract eat():void;
}

class Cat extends Animal {
constructor(name:string) {
super(name)
}

eat() {
console.log(this.name+"吃鱼")
}
}

class Dog extends Animal {
constructor(name:string) {
super(name)
}

eat() {
console.log(this.name+"吃肉")
}
}

let cat = new Cat("cat")

let dog = new Dog("dog")

cat.eat()
dog.eat()

接口

TypeScript的核心原则之一是对值所具有的结构进行类型检查。

而接口在这里的作用就是提供自定义的第三方契约

1
2
3
4
5
6
7
8
9
10
interface LabelledValue {
label: string
}

function printLabel(labelledObj: LabelledValue) {
console.log(lavelledObj.label);
}

let myObj = {size: 10, lebel: "Size 10 Object"};
printLabel(myObj); //Size 10 Object

上面的例子中,接口LabelledValue更像是一个自定义的数据类型集合,这里的接口不想其他语言一样是由函数来继承实现的,而是直接规定了值的类型,只要值的属性与类型是对的,那么就是被允许的

类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型是对的就可以了

额外的属性检查和索引签名

1
2
3
4
5
6
7
8
9
10
interface SquareConfig {
color?: string;
width?: number;
}

function createSquare(config: SquareConfig): {color:string; area: number} {

}

let mySquare = createSquare({colour: "red", width: 100});

细心观察我们会发现这里传入的时colour而不是color,

在javaScript中我们会认为这段代码是完全失败的,color在SquareConfig接口中是可选属性,可以不存在,width作为属性传入没有异议,但是接下来我们就要讨论colour了,这个属性直观上我们肯定觉得这个属性应该直接被编译器忽略了,是没有意义的,但是在TypeScript中就可能存在bug,对象字面量在TypeScript中会被特殊对待,会经过额外的属性检查,如果此时一个字面量存在“目标类型”中不包含的属性,那么就会得到一个错误:

1
2
let mySquare = createSquare({colour: "red", width: 100});
// error: 'colour' not expected in type 'SquareConfig'

而绕开这个检查最佳简便的办法就是使用类型断言:

1
let mySquare = createSquare({colour: "red", width: 100} as SquareConfig);

最佳方式其实是使用字符串索引签名,,如果SquareConfig中带有上面定义的类型,且还会携带任意数量的其他属性,那么我们可以这样定义:

1
2
3
4
5
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}

使用索引签名就是能够规定,我们只对接收的color和width属性有所约束,而对于其他属性没有类型限制

还有一种方式就是将colour赋予另外一个对象变量squareOption,然后在传参时使用squreOption进行传入,这样就能绕过额外的属性检查

1
2
let squareOption = {colour: "red", width: 100};
let mySquare = createSquare(squareOption);

接口继承

与类一样,接口也是可以进行继承的

1
2
3
4
5
6
7
8
9
10
11
interface Shape {
color: string;
}

interface Square extends Shape {
sideLength: number;
}

let square = {};
square.color = "blue";
square.sideLength = 100;

一个接口可以继承多个接口,那么它就拥有所有接口的属性

泛型

泛型可以用于约束函数传入的数据类型

1
2
3
4
5
6
7
8
9
10
11
12
//未使用泛型时
const getArray = (value: any, times: number = 5) => {
return new Array(times).fill(value)
}
console.log(getArray(123,4).map((item) => item.toFixed())) //["123", "123", "123", "123"]

//使用泛型后
const getArray = (value: T,times: number = 5): T[] => {
return new Array(times).fill(value)
}
console.log(getArray<number>(123,4).map((item) => item.toFixed())) // ["123", "123", "123", "123"]
console.log(getArray<number>("asd",4).map((item) => item.toFixed())) //Error