概述
js开发中存在的问题
- 使用了不存在的变量、函数或成员;
- 把一个不确定的类型当做一个确定的类型处理;
- 在使用null或undefined的成员。(TypeError: Cannot- read property 'name' of undefined,在统计的前端开发排名前10的错误中,这个错误排第一);
js和ts代码对比
- 看下面简单的一段程序,将姓名的首字母大写后反转返回;
下面的程序中有几处错误,如果在js中,我们只有在代码运行时才能发现错误;
function getUserName() {
if (Math.random() < 0.5) {
return 'zhang yin'
};
return 404;
}
let myname = getUsername();
mynema = myname.split(' ').map(el => el[0].touppercase + el.subStr(1)).join(' ');
- 现在什么都不用做,只把js后缀换成ts,错误立刻就显示出来了,ts不会等到执行时才报错。
- 点击快速修复
- 还有错误,提示myname可能是string或者number,number上没有split方法。加判断
- 还有错误,一直点快速修复
- 完美,错误全部修复
js的原罪
- js语言本身的特点,决定了该语言无法适应大型的复杂的项目;
- 弱类型:某个变量,可以随时更换类型;
- 解释性:错误发生的时间,是在运行时;
前端开发中,大部分时间都是在排错;
TypeScript (ts = js + 类型系统)
- TypeScript是js的超集,是一个可选的、静态的类型系统;
- 超集:包含的关系,整数是正整数的超集;
- 类型系统:对代码中所用的标识符(变量、函数、参数、返回值)进行类型检查;
- 可选的,学习曲线相对平滑;
- ts不参与任何运行时的类型检查;
- ts在编写时报错,js在执行时报错;
ts的常识
- 2012年微软发布;
- Anders Hejlsberg 负责开发ts项目;
- 开源的,拥抱ES标准;
额外的惊喜
- 有了类型检查,无形中增强了面向对象的开发;
- js中也有类和对象,js支持面向对象开发,没有类型检查,很多面向对象的场景实现起来会有诸多问题;
- 使用ts后,可以编写出完善的面向对象代码;
参考文献
在node中搭建ts开发环境
安装TypeScript
npm i typescript -g
将ts编译成js
tsc index.ts
默认情况下,TS会做出下面几种假设:
- 假设当前的执行环境是浏览器环境(Dom);
- 如果代码中没有使用模块化语句(import、export),便认为该代码是时全局执行;
- 默认编译的目标代码是ES3;
解决方法
- 使用tsc命令行的时候,加上选项参数;
- 使用ts配置文件,更改编译选项;
TypeScript的配置文件
生成tsconfig.json
tsc --init
使用了配置文件后,使用tsc进行编译时,不能跟上文件名,如果跟上文件名,会忽略配置文件
@types/node
npm i -D @types/node
@types/node是一个ts官方的类型库,其中包含了很多对js代码的类型描述;
例:
JQuery:用js写的,没有类型检查;
安装@types/jquery,为jquery库添加类型定义;
使用第三方库简化流程
ts-node:将ts代码在内存中完成编译,同时完成运行
npm i ts-node -g ts-node /src/index.ts
nodemon:用于检测文件的变化
npm i nodemon -g
nodemon --exec ts-node sec/index.ts
写成script脚本:
"dev": "nodemon --watch src -e ts --exec ts-node src/index.ts" // 只监控src下的ts文件
基本类型检查
类型约束和编译结果对比
编译结果中没有类型约束信息;
如何进行类型约束
- ts 是一个可选的静态类型系统;
- ts 在很多场景中可以完成类型推导;
- any 表示任意类型,ts 不进行类型约束;
- 如何区分数字字符串和数字,关键看怎么读?如果按照数字的方式朗读,则为数字,否则为字符串
基本类型检查
- number
- string
- boolean
- array
- object
- null 和 undefined 是所有其它类型的子类型,它们可以复制给其它类型;
"strictNullChecks": true, 可以获得更严格的空类型检查,null 和 undefined 只能赋值给自身
其它常用类型
- 联合类型:多种类型任选其一 ;
- 配合类型保护进行判断;
- 类型保护:当对某个变量进行类型判断之后,在判断的语句块中便可以确定它的确切类型,typeof 可以触发类型保护;
let name: string | undefined = undefined;
if (typeof name === "string") {
name.toUpperCase();
};
- void 类型:通常用于约束函数的返回值,表示该函数没有任何返回;
- never 类型:通常用于约束函数的返回值,表示该函数永远不会结束;
function demo () {
throw new Error("错误");
console.log("永远不会执行");
};
function demo1 () {
while(true){
cconsole.log('never')
}
};
- 字面量类型: 使用一个值进行约束
let arr: []; // arr永远只能取值为一个空数组
let gender: '男' | '女';
let user: {
name: string
age: number
} // 一个对象,必须要name和age
- 元祖类型(Tuple):一个固定类型的数组,并且数组中每一项的类型确定
let arr: [string, number];
arr = ['zy', 21];
- any类型:可以绕过类型检查,any类型可以赋值给任意类型
let name: any = 'zhangyin';
let age: number = any;
类型别名
类型别名:对已经类型定义名称;
type Gender = 'man' | 'womam';
type User = {
name: string
age: number
gender: Gender
}
let person: User;
person = {
name: 'zhangyin',
gender: 'man',
age: 21,
};
function getUsers (g: Gender): User[] {
return[];
}
函数的相关约束
- 函数重载:在函数实现之前,对函数调用的多种方式进行声明
function combine (a: number, b: number): number;
function combine (a: string, b: string): string;
function combine (a: number | string, b: number | string): number | string {
if (typeof a === 'number' && typeof b === 'number') {
return a * b;
}
else if (typeof a === 'string' && typeof b === 'string') {
return a + b;
}
throw new Error('a和b必须是相同的类型');
}
const result = combine(3, 4);
- 可选参数和默认值:可以在某些参数后加上问号,表示该参数可以不用传递
function sum (a: number, b: number, c?: number = 0) {
if (c) {
return a + b + c;
} else {
return a + b;
}
};
demo:创建并打印扑克牌
目标:创建一副扑克牌(不包括大小王),打印该扑克牌;
type Deck = Card[];
type Color = "♠" | "♥" | "♣" | "♦";
type Card = {
color: Color;
mark: number;
};
function createDeck(): Deck {
const deck: Deck = [];
for (let i = 1; i <= 13; i++) {
deck.push({
mark: i,
color: "♠"
});
deck.push({
mark: i,
color: "♣"
});
deck.push({
mark: i,
color: "♥"
});
deck.push({
mark: i,
color: "♦"
});
}
return deck;
}
function printDeck(deck: Deck) {
let deckStr = "";
deck.forEach((el, i) => {
if ((i + 1) % 6 === 0) {
deckStr += el.color + el.mark + "\n";
} else {
deckStr += el.color + el.mark + "\t";
}
});
console.log(deckStr);
}
printDeck(createDeck());
打印结果