TS简介及安装
TypeScript是什么?
TypeScript,简称TS,是JavaScript的超集,主要提供了类型系统和对ES6的支持。简单来说JS和TS 的关系就是,JS是TS的子集。
- TypeScript是以JavaScript为基础构建的语言
- TypeScript拓展了JavaScript,并添加了类型
- TypeScript可以在任何支持JavaScript的平台中运行
- TypeScript不能被JS解析器直接执行
TypeScript的优势
- 类型化思维方式,更严谨,能够提前发现错误,减少改bug的时间
- 代码可读性提高,便于解析和重构
- 补充了接口、枚举等功能
TypeScript的弊端
- 有一定的学习成本,需要理解接口、泛型、类、枚举类型等前端工程师不太熟悉的概念。
- 短期会增加开发成本,但对于一个长期项目,TS会减少维护成本。
TypeScript的安装及执行
安装TS
在VScode中安装打开终端,输入命令行:npm install -g typescript 装好后我们新建一个TS文件,例如新建一个hello.ts文件
执行TS
两步命令执行TS代码
- tsc hello.ts TS代码->JS代码,生成一个hello.js文件
- node hello.ts 执行hello.js文件中的代码
之后生成对应的.js文件
简化执行TS代码
- 使用ts-node包,直接在node.js中执行TS 代码
- 安装ts-node包: npm i -g ts-node
- 简化执行命令:ts-node hello.ts
TS代码不能直接在node.js执行
TS代码->JS代码->执行JS
TS基础知识
基础数据类型
原始数据类型
布尔值、数值、字符串、null、undefined、Symbol(ES6中新类型)
布尔值
布尔值是最基础的数据类型,在TS中我们用boolean定义布尔值类型:
let isDone : boolean = false;
布尔值只有两个值:true 、false
数值
在TS中我们使用number定义数值类型:
let decLiteral : number = 620;
let binaryLiteral : number = 0b0b10;
let octalLiteral : number = 0o744
字符串
使用string类型定义字符串: 单引号、双引号都可
let myName : string = "ailin" ;
空值
JavaScript中没有空值void的概念,但在TypeScript中,我们可以使用void来表示没有任何返回值的函数,声明一个void类型的变量没什么用,因为我们只能给变量赋值为undefined和null,所以我们一般不给变量声明void类型
Null和Undefined
let u : undefined = undefined;
let n : null = null;
null和Undefined与void的不同点在于:null和Undefined是所有类型的子类型,也就是说我们可以将undefined类型的变量赋值给其他类型的变量:
let num: number = undefined;
let u : undefined;
let num1: number = u;
let v : void;
let num : number= v;
任意值
任意值any类型的变量用于表示允许赋值为任意类型,但是其他普通类型是不允许被赋值为其他类型的。
let myFavouriteNumber : any = 'six';
myFavouriteNumber = 6;
let myFavouriteNumber : string = "six";
myFavourite = 6;
变量在声明时未指定数据类型,会被识别为任意值类型。
let something;
something = 'six';
something = 6;
something.setName('ailin');
let something : any;
something = 'six';
something = 6;
something.setName('ailin');
类型推论
类型推论:TypeScript会在没有明确的指定类型时推测出一个类型。 在coding中,如果没有给出明确的指定类型,那么TS会依据类型推论(Type Inference)的规则判断出变量类型。 定义变量时赋值,会根据赋值推论类型
let myFavouriteNumber = 'six';
如果定义变量时没有赋值,不管这个变量之后有没有被赋值,它都会被推断出any类型。
联合类型
联合类型表示一个变量可以有多种类型。
let myFavouriteNumber: string | number;
myFavourite = 'six';
myFavourite = 6;
在联合类型中,我们用|分割每个类型。
访问联合类型的属性或方法
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。 联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型。
对象的类型–接口
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。 这个例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。即对应的属性不能多也不能少,多了少了都会报错。 所以赋值的时候,变量的形状必须和接口的形状保持一致。
可选属性
但有的时候我们希望不要完全匹配一个形状,那我们就可以用可选属性。
age就是可选属性,对象可以不用这个属性,但不可以添加新的未定义的属性,即属性可少但不能多。
数组
数组的定义方式:
类型+方括号表示法:
let num : number[] = [6,2,0];
let num : number[] = [6,'2',0]
数组泛型表示法
let num : Array<number> = [6,6,2,2,0,0,];
接口表示法
一般不用接口表示数组,相比较前两种方法太过复杂。
interface NumberArray {
| [index:number]: number;
}
let num:NumberArray = [6,6,2,2,0,0];
any在数组中的应用
let list : any[] = ['ailin', 21];
类数组
类数组不是数组类型,不能用普通数组的方式来描述,应该用接口。 我们将arguments定义为一个类数组:
function sum() {
| let ming: {
|||[index: number]:number;
| } = arguments;
}
函数
函数的定义
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
}
可选参数
可选参数 与接口中的可选属性类似,用 ? 表示可选的参数。 需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了。
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('tom', 'cat');
let tom = buildName('tom');
function buildName(firstName?: string, lastName: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('tom', 'cat');
let tom = buildName('tom');
参数默认值
在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数,但是在这时,不受「可选参数必须接在必需参数后面」的限制。
function buildName(firstName: string, lastName: string = 'cat') {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('tom', 'cat');
let tom = buildName('tom');
function buildName(firstName: string = 'tom', lastName: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('tom', 'cat');
let tom = buildName(undefined, 'cat');
剩余参数
有一种情况,我们不知道要向函数传入多少个参数,这时候我们就可以使用剩余参数来定义。剩余参数语法允许我们将一个不确定数量的参数作为一个数组传入。 函数的最后一个命名参数 restOfName 以 … 为前缀,它将成为一个由剩余参数组成的数组,索引值从0(包括)到 restOfName.length(不包括)。
function buildName(firstName: string, ...restofName: string[]) {
return firstName + ' ' + restofName.join(" ");
}
let importantName = buildName("lal", "xjj", "lyx", "slf", "cj", "cy");
元祖
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。 比如定义一对值分别为 string 和 number 的元组:
let tom: [string, number] = ['tom', 21];
let tom: [string, number];
tom[0] = 'tom';
但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。
let tom: [string, number];
tom = ['tom'];
当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型:
let tom: [string, number];
tom = ['tom', 21];
tom.push('male');
tom.push(true);
枚举
应用
枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。
enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
未手动赋值的枚举项会接着上一个枚举项递增。
如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的:
enum Days { Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat };
console.log(Days["Sun"] === 3);
console.log(Days["Wed"] === 3);
console.log(Days[3] === "Sun");
console.log(Days[3] === "Wed");
上面的例子中,递增到 3 的时候与前面的 Sun 的取值重复了,但是 TypeScript 并没有报错,导致 Days[3] 的值先是 “Sun”,而后又被 “Wed” 覆盖了。 当然,手动赋值的枚举项也可以为小数或负数,此时后续未手动赋值的项的递增步长仍为 1:
类型断言
用途
类型断言的常见用途有以下几种:
- 将一个联合类型断言为其中一个类型
- 将任何一个类型断言为 any
- 将 any 断言为一个具体的类型
注意点
需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误:
原因是 (animal as Fish).swim() 这段代码隐藏了 animal 可能为 Cat 的情况,将 animal 直接断言为 Fish 了,而 TypeScript 编译器信任了我们的断言,故在调用 swim() 时没有编译错误。 可是 swim 函数接受的参数是 Cat | Fish,一旦传入的参数是 Cat 类型的变量,由于 Cat 上没有 swim 方法,就会导致运行时错误了。 总之,使用类型断言时一定要格外小心,尽量避免断言后调用方法或引用深层属性,以减少不必要的运行时错误。
举例
const foo: number = 1;
foo.length = 1;
const foo: number = 1;
window.foo = 1;
const foo: number = 1;
(window as any).foo = 1;
在 any 类型的变量上,访问任何属性都是允许的。
总结
需要注意的是,将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。 它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。 上面的例子中,我们也可以通过[扩展 window 的类型(TODO)][]解决这个错误,不过如果只是临时的增加 foo 属性,as any 会更加方便。 总之,一方面不能滥用 as any,另一方面也不要完全否定它的作用,我们需要在类型的严格性和开发的便利性之间掌握平衡(这也是 TypeScript 的设计理念之一),才能发挥出 TypeScript 最大的价值。
|