前言
本文的目的是在回顾前文的学习中,发现一些当时没有注意到的细节,这些细节在上两文中没有写到,现在将这些细节记录下来以供后续学习;
环境
TypeScript必须要安装node环境的,并且也是要借助于npm去安装一些依赖包的,如果是集成在Vue或者React中的TypeScript,它的一些依赖包实际上已经在搭建的时候就已经一起安装进去了
NodeJs安装
自行登录NodeJS的官网,下载对应系统的安装包,直接下一步下一步就可以安装完毕,官网地址是:NodeJS官网,安装完毕后,可以通过以下命令查看是否安装成功
node查看
node -v
npm查看
npm -v
格式化插件
可以说,现在的前端,vscode绝对有一席之地,它的好处就不再多说了,我们在编写typescript代码的时候,经常需要格式化代码,格式化后的代码更方便阅读和书写,而格式化的插件在vscode中推荐:
// 插件名称
Prettier - Code formatter
这个插件可以非常好的格式化各种的代码,无论是JS代码还是TS代码,强烈推荐
依赖包
众所周知的原因,TypeScript的使用需要安装一些依赖,首先是TypeScript本体,当然,这边说的是独立的项目,不是基于Vue或者React,在Vue或者React中如果选择是TS的项目,那么这些依赖会被框架自动安装,
安装本体
npm install typescript -g // 全局安装
安装成功后,可以输入以下命令进行验证
tsc -v
安装编译包
.ts文件是不会被直接识别的,正常情况下,如果想要使用,必须先编译,假如ts文件的名字是app.ts,直接
node app.ts // 报错
会报错,因为.ts文件没有被识别,如果要使用,那么必须要编译,编译的命令,就是在对应的路径下
tsc app.ts
那么ts就会对app.ts进行编译,将其编译成app.js文件,并且所有在app.ts中用到的关联文件都会被一起编译,但是这样很不方便,如果该一点代码看效果就要编译一次,那么效率就会很低,因此需要安装另外一个依赖ts-node
npm install ts-node -g // 全局安装
安装成功后,如果需要运行app.ts文件,那么再次输入命令
node app.ts // 正确执行
类型
基本类型
在定义基本类型的时候,比如:
let num:number = 1;
对变量进行属性定义后,那么这个变量继承到的方法一定是类型上存在的,比如上例中定义成了number,当我们在后续代码中要对num变量进行操作的时候,比如将数字转成字符串的方法toString(),这个是可以的,因为在number上存在toString这个方法,如果要使用toFunction()则会报错,因为number上不存在toFunction()这个方法
num.toString(); // 可以
num.toFunction(); // 不可以
这便是TS中的一个优势,在你编写代码的时候,就可以检查出代码的用法是否可行;
对象类型
如果一个变量,它是某个构造函数的实例,这种如何定义类型,实际如下
// 构造函数
class School{}
// 定义
const mySchool:School = new School()
函数类型
// 定一个函数,并且存在返回值,返回值的类型是数字
const getAge:()=>number = ()=>{
return 10
}
另外函数有一个非常特殊的返回类型:never,这个类型代表这个函数永远不会执行到最后一行代码,比如:
function errorMsg():never{
throw new Error("报错");
console.log("最后一行");
}
在这个代码中,就不会执行console.log,因为上面一行代码之前抛异常了,因此这种函数就是never类型;
另外,在ES6中有一个解构的语法,那么解构的语法怎么定义类型,实际使用如下:
function add({first,second}){
return first + second;
}
const total = add({first:1,second:2});
// 错误
function add({ first:number, second:number}){
return first + second;
}
// 正确
function add({ first, second}:{first:number,second:number}){
return first + second;
}
数组类型
// 正确
const arr:(number|string)[] = ["1",2,3];
// 报错,因为没有定义number类型
const arr:(undefined|string)[] = [undefined,1,"2"]
可以通过小括号,来定义联合类型的数组,然后,再来一个复杂一点的示例
class Demo{
name:string;
age:number
}
const demoArr:(Demo|string)[] = [
new Demo({
name:"test",
age:99
}),
"demo"
]
再来一个示例,考验一下:
// 题目1:
const demo = ["oliver","demo",20];
// 题目2:
const demoList = [
["1","2",3],
["oliver","demo",20],
["test","demo",30]
];
解答:
// 题目1
const demo:[string,string,number] = ["oliver","demo",20];
// 题目2
const demoList:[string,string,number][] = [
["1","2",3],
["oliver","demo",20],
["test","demo",30]
];
DOM类型
DOM类型一般都是HTMLElement,比如下方代码示例:
const curEl: HTMLElement = document.createElement("div");
如果是事件类型,比如给window添加了一个按键事件,那么类型就是对应的类型KeyboardEvent
window.addEventListener("keydown", handleKeyDown);
function handleKeyDown(event: KeyboardEvent){
// ...代码
}
类型注解和类型推断
类型注解:说到底,就是开发者主动告诉TS,这个变量是一个什么类型,比如
let num:number;
num = 20;
num = "20"; // 报错,因为已经定义成了number类型
在定义变量的时候,直接告诉TS,这是一个number类型,不要弄错了,那么后面使用的时候都只能是number类型
类型推断:同样,如果我们在定义变量的时候没有定义类型,那么TS会在我们赋值的时候自动将这个值的类型赋予这个变量,比如
let number = 20; // 此时的numberTS会自动将其定义成number类型
number = "20"; // 报错
如果TS能自动分析出变量的类型,那么我们就没有必要去进行类型的定义,但是如果TS无法分析出变量的类型,那么我们就要通过类型注解的方式将其的类型定义完善;
我们在使用TS的过程,本身就是在开发层面去完善每一个变量的类型,这样,对应代码的BUG率就会大幅降低;
接口
接口一般是这么定义的
inteface Person{
name:string;
age:number;
}
但是,实际开发中,假如这个Person的数据是从后端接口拿到的,数据中往往还有别的一些参数,比如sex,那么此时对变量进行定义类型Person就会报错,因为Person中不存在sex属性,那么这种不可预知的情况,或者说需要随时扩展的情况怎么办,那么可以尝试定义成下面类型
inteface Person{
name:string;
age:number;
[propName:string]:any
}
接口类型也是能去描述函数的,比如
interface Say{
(word:string):string
}
// 应用接口
const say:Say = (word:string) = > {
return word;
}
这个接口代表着,定义了一个接口,名字叫做Say,它接受一个word作为参数,参数的类型是string,并且这个函数存在返回值,返回值的类型也是string