超全TypeScript学习笔记,入门新手看这里~

TS简介及安装

TypeScript是什么?

TypeScript,简称TS,是JavaScript的超集,主要提供了类型系统和对ES6的支持。简单来说JS和TS 的关系就是,JS是TS的子集。

  1. TypeScript是以JavaScript为基础构建的语言
  2. TypeScript拓展了JavaScript,并添加了类型
  3. TypeScript可以在任何支持JavaScript的平台中运行
  4. TypeScript不能被JS解析器直接执行

TypeScript的优势

  1. 类型化思维方式,更严谨,能够提前发现错误,减少改bug的时间
  2. 代码可读性提高,便于解析和重构
  3. 补充了接口、枚举等功能

TypeScript的弊端

  1. 有一定的学习成本,需要理解接口、泛型、类、枚举类型等前端工程师不太熟悉的概念。
  2. 短期会增加开发成本,但对于一个长期项目,TS会减少维护成本。

TypeScript的安装及执行

安装TS

在VScode中安装打开终端,输入命令行:npm install -g typescript
装好后我们新建一个TS文件,例如新建一个hello.ts文件

执行TS

两步命令执行TS代码

  1. tsc hello.ts TS代码->JS代码,生成一个hello.js文件
  2. node hello.ts 执行hello.js文件中的代码
    之后生成对应的.js文件

简化执行TS代码

  1. 使用ts-node包,直接在node.js中执行TS 代码
  2. 安装ts-node包: npm i -g ts-node
  3. 简化执行命令: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;
//二进制表示法 10
let binaryLiteral : number = 0b0b10;
//八进制表示法 484
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;
//报错:不能将类型"void"赋值给number类型的变量
任意值

任意值any类型的变量用于表示允许赋值为任意类型,但是其他普通类型是不允许被赋值为其他类型的。

let myFavouriteNumber : any = 'six';
myFavouriteNumber = 6;
//以上代码正确
let myFavouriteNumber : string = "six";
myFavourite = 6;
//报错:不能将类型"number"分配给"string"

变量在声明时未指定数据类型,会被识别为任意值类型。

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';//类型推论为string类型

如果定义变量时没有赋值,不管这个变量之后有没有被赋值,它都会被推断出any类型。

联合类型

联合类型表示一个变量可以有多种类型。

//比如变量myFavouriteNumber就有两个类型string类型和number类型,但除了这两个类型外,不能有其他类型的赋值。
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]
//报错:不能将"string"类型分配给类型"number"
数组泛型表示法
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'];
//报错:不能将类型"string"分配给类型"[string,number]"

当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型:

let tom: [string, number];
tom = ['tom', 21];
tom.push('male');//正确
tom.push(true);//报错:类型"boolean"的参数不能赋给类型"string|number"的参数

枚举

应用

枚举(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);//true
console.log(Days["Wed"] === 3);//true
console.log(Days[3] === "Sun");//false
console.log(Days[3] === "Wed");//true

上面的例子中,递增到 3 的时候与前面的 Sun 的取值重复了,但是 TypeScript 并没有报错,导致 Days[3] 的值先是 “Sun”,而后又被 “Wed” 覆盖了。
当然,手动赋值的枚举项也可以为小数或负数,此时后续未手动赋值的项的递增步长仍为 1:
在这里插入图片描述

类型断言

用途

类型断言的常见用途有以下几种:

  1. 将一个联合类型断言为其中一个类型
  2. 将任何一个类型断言为 any
  3. 将 any 断言为一个具体的类型

注意点

需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误:
在这里插入图片描述

原因是 (animal as Fish).swim() 这段代码隐藏了 animal 可能为 Cat 的情况,将 animal 直接断言为 Fish 了,而 TypeScript 编译器信任了我们的断言,故在调用 swim() 时没有编译错误。
可是 swim 函数接受的参数是 Cat | Fish,一旦传入的参数是 Cat 类型的变量,由于 Cat 上没有 swim 方法,就会导致运行时错误了。
总之,使用类型断言时一定要格外小心,尽量避免断言后调用方法或引用深层属性,以减少不必要的运行时错误。

举例

//下面的例子中,数字类型的变量 foo 上是没有 length 属性的,故 TypeScript 给出了相应的错误提示。
const foo: number = 1;
foo.length = 1;
//上面的例子中,我们需要将 window 上添加一个属性 foo,但 TypeScript 编译时会报错,提示我们 window 上不存在 foo 属性。
const foo: number = 1;
window.foo = 1;
//此时我们可以使用 as any 临时将 window 断言为 any 类型:
const foo: number = 1;
(window as any).foo = 1;
//正确

在 any 类型的变量上,访问任何属性都是允许的。

总结

需要注意的是,将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。
它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。
上面的例子中,我们也可以通过[扩展 window 的类型(TODO)][]解决这个错误,不过如果只是临时的增加 foo 属性,as any 会更加方便。
总之,一方面不能滥用 as any,另一方面也不要完全否定它的作用,我们需要在类型的严格性和开发的便利性之间掌握平衡(这也是 TypeScript 的设计理念之一),才能发挥出 TypeScript 最大的价值。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二琳爱吃肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值