1.1 TypeScript 简介
- TypeScript 是 JavaScript 的超集,它可以编译成纯 JavaScript。
- TypeScript 基于 ECMAScript 标准进行拓展,支持 ECMAScript 未来提案中的特性,如装饰器、异步功能等
- TypeScript 编译的 JavaScript 可以在任何浏览器运行,TypeScript 编译工具可以运行在任何操作系统上。
- TypeScript 起源于开发较大规模 JavaScript 应用程序的需求。由微软在2012年发布了首个公开版本。
1.2 TypeScript 安装与使用
1.2.1 环境准备
- 安装
Node.js
环境(version: 8.14.0+) - 确保
npm
或者yarn
可用
到 nodejs官网 根据自己的操作系统下载对应 Node.js 版本,Node.js 自带 npm。安装后,在 终端
执行如下命令,检查是否安装成功:
~ node -v
~ npm -v
1.2.2 TypeScript 安装
通过 npm 全局安装 TypeScript:
npm install -g typescript
TypeScript 在全局安装后,我们可以在任意位置使用 tsc
命令,tsc
命令负责编译 TypeScript 文件为 JavaScript 文件。
注意:可以修改npm的资源镜像链接
npm config set registry http://registry.npm.taobao.org
查看版本命令:
tsc -v
1.2.3 安装直接运行的包
npm install -g ts-node
1.2.4 TypeScript 使用
使用webstorm创建一个项目ts-xueden,新建一个helloworld.ts文件,代码如下:
let message = "Hello World";
console.log(message);
1.2.5 在webstorm设置中安装插件ts-node
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTjCK48I-1614911646305)(images/1885351-20191213165259880-1594658187.jpg)]
第2章 TypeScript基础语法
2.1 TypeScript 变量声明
TypeScript 是 JavaScript 的超集,同 JavaScript 一样,声明变量可以采用下面三个关键字:
2.1.1 使用var 声明变量
var [变量名] : [类型] = 值;
如:var uname:string = "xueden";
声明变量的类型,但没有初始值,变量值会设置为 undefined:
var [变量名] : [类型];
如:var uname:string;
声明变量并初始值,但不设置类型,该变量可以是任意类型:
var [变量名] = 值;
如:var uname = "xueden";
声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined:
var [变量名];
如:var uname;
**注意:**变量不要使用 name 否则会与 DOM 中的全局 window 对象下的 name 属性出现了重名。
2.1.1.1 var 作用域
变量作用域: 指定了变量定义的位置, 程序中变量的可用性由变量作用域决定。
TypeScript 有以下几种作用域:
全局作用域 − 全局变量定义在程序结构的外部,它可以在你代码的任何位置使用。
类作用域 − 这个变量也可以称为 字段。类变量声明在一个类里头,但在类的方法外面。 该变量可以通过类的对象来访问。类变量也可以是静态的,静态的变量可以通过类名直接访问。
局部作用域 − 局部变量,局部变量只能在声明它的一个代码块(如:方法)中使用。
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100 * i)
}
大多数人期望输出的结果是这样的:
0
1
2
3
4
5
6
7
8
9
然而,实际结果是这样的:
10
10
10
10
10
10
10
10
10
10
解释:这里的 i
是在全局作用域中的,只存在一个值。 setTimeout
这个异步函数在若干毫秒后执行,并且它是在 for
循环结束后的。在 for
循环结束后, i
的值为 10
,所以当函数被调用的时候,它会打印出 10
。
下面介绍几种可以实现我们预期输出结果的方法:
1、 通过调用函数,创建函数作用域
for (var i = 0; i < 10; i++) {
f(i)
}
function f (i) {
setTimeout(function () {
console.log(i)
}, 100)
}
2、立即执行函数,创建函数作用域:
for (var i = 0; i < 10; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, 100 * i)
})(i)
}
2.1.1.2 var 变量的提升
在代码中 使用 var 来声明变量的时候,会提到当前作用域的顶端,而赋值操作在原处不变
代码如下:
console.log(a) // undefined
var a = 1
它等价于:
var a
console.log(a) // undefined
a = 1
从上面的例子中可以看出,关键词 var
会进行变量提升。
var a声明向上提升,a=1赋值留在原处
2.1.2 使用let 声明变量
通过上面的学习,我们知道了 var
存在全局作用域和变量提升的问题,这恰好说明了为什么用 let
语句来声明变量。
let [变量名] : [类型] = 值;
如:let uname:string = "xueden";
声明变量的类型,但没有初始值,变量值会设置为 undefined:
let [变量名] : [类型];
如:let uname:string;
声明变量并初始值,但不设置类型,该变量可以是任意类型:
let [变量名] = 值;
如:let uname = "xueden";
声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined:
let [变量名];
如:let uname;
2.1.2.1 let作用域
function block() {
if (true) {
let a = 10
console.log(a) // 10
}
console.log(a) // Cannot find name 'a'.ts(2304)
}
2.1.3 使用 const 声明变量
关键字 const 声明变量,它被赋值后不能再改变。 换句话说,它拥有与 let 相同的作用域规则,但是不能重新赋值。
用 const 声明变量,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
用 const 声明初始数据类型如布尔值、数字、字符串,可以理解为声明常量,因为这些初始类型的值就保存在变量所指向的那个内存地址。
代码如下所示:
const num = 10
num = 20
对于复合类型的数据来说,变量所指向的内存地址保存的只是一个指针,const 能够保证其指针不变,但属性值是可变的:
代码如下:
const person = {
name: '张三',
address: '中国广州'
}
// error
person = {
name: '李四',
address: '中国上海'
}
// ok
person.name = '王五'
2.2 TypeScript 基础类型
从本节起,我们将开始接触 TypeScript 的类型系统,这也是 TypeScript 最为核心的部分。
2.2.1 TypeScript数据类型
在TypeScript中有以下数据类型
- boolean ( 布尔类型 )
- number( 数字类型 )
- string( 字符串类型 )
- void( void类型 )
- null
- undefined
- bigint
- symbol
以上是 原始类型
- 元组 tuple
- 枚举 enum
- 任意 any
- unknown
- never
- 数组 Array
- 对象 object
2.2.1.1 boolean布尔类型
变量声明语法:冒号 :
前面是变量名称,后面是变量类型。
const isDelete: boolean = false
const state: boolean = Boolean(0)
2.2.1.2 number数字类型
二进制数、十进制数、十六进制数都可以用 number
类型来表示。
let num1: number = 6// 十进制
let num2: number = 0.618
let num3: number = 0xf00d// 十六进制
let num4: number = 0b1010// 二进制
let num5: number = 0o744// 八进制
let num6: number = NaN
2.2.1.3 string 字符串类型
双引号或者单引号表示字符串:
let username: string = "张三"
let addr: string = '中国广州'
使用模板字符串:
let xueden: string = `我叫${username}住在${addr}`
模板字符串使用反引号来代替,普通字符串中的用双引号和单引号。
模板字符串可以包含特定语法 ${expression}
的占位符,占位符内可以写变量名,模板字符串会进行变量值的解析。
2.2.1.4 void 类型
当一个函数没有返回值时,可以将其返回值类型定义为 void:
function doNothing(): void {
let a = 10
}
声明一个 void
类型的变量没有什么用,因为你只能将它赋值为 undefined
和 null
:
let nothing: void = undefined
2.2.1.5 null 类型和 undefined 类型
1、undefined
和 null
是所有类型的子类型, 可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined。
2、在 JavaScript 中 null 表示 “什么都没有”。
3、null是一个只有一个值的特殊类型。表示一个空对象引用。
4、 用 typeof 检测 null 返回是 object。
5、 在 JavaScript 中, undefined 是一个没有设置值的变量。
6、 typeof 一个没有值的变量会返回 undefined。
在TypeScript中启用严格的空校验(–strictNullChecks)特性,就可以使得null 和 undefined 只能被赋值给 void 或本身对应的类型,
新建tsconfig.json文件,代码如下所示
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"strictNullChecks": true
},
"exclude": [
"node_modules"
],
"include": [
"src/**/*"
]
}
示例代码如下:
// 启用 --strictNullChecks
let x: number;
x = 1; // 运行正确
x = undefined; // 运行错误
x = null; // 运行错误
console.info(a)
上面的例子中变量 x 只能是数字类型。如果一个类型可能出现 null 或 undefined, 可以用 | 来支持多种类型,示例代码如下:
// 启用 --strictNullChecks
let x: number | null | undefined;
x = 1; // 运行正确
x = undefined; // 运行正确
x = null; // 运行正确
2.2.1.6 数组类型
数组类型有两种表示方法,第一种在元素类型后接上 []
,表示由此类型元素组成的一个数组:
let list: number[] = [1, 2, 3]
let names: string[] = ['xueden', 'java', 'ts']
另一种方式是使用数组泛型Array<元素类型>
:
let list: Array<number> = [1, 2, 3]
let names: Array<string> = ['xueden', 'java', 'ts']
混合各种元素类型:
let list: any[] = ['xueden', 2021]
推荐使用第一种数组类型的表示方法,书写比较简洁直观。
2.2.1.7 any 类型
有时候接收来自用户的输入,我们是不能确定其变量类型的。这种情况下,我们不希望类型检查器对这些值进行检查,而是直接让它们通过编译阶段的检查,此时可以使用 any
:
let x: any = 1; // 数字类型
x = 'xueden'; // 字符串类型
x = false; // 布尔类型
2.2.1.8 object 类型
object
表示非原始类型
let obj: object
// 枚举类型
enum TokenType {
ACCESS = 'accessToken',
REFRESH = 'refreshToken'
}
obj = TokenType
obj = [1, 2, 3]
obj = [1, 'string'] // 元组类型
obj = { a: 1 }
可以看到枚举、数组、元组和普通对象都是 object
类型。
注意:
1、TypeScript 中描述类型要用 小写,比如 boolean、number、string等;
2、大写开头的如 Boolean、Number、String 代表的是 JavaScript 的构造函数:
2.2.1.9 bigint 类型
bigint 是一种基本数据类型 ,是用来表示那些已经超出了 number 类型最大值的整数值
JavaScript 中可以用 Number
表示的最大整数为 2^53 - 1
,可以写为 Number.MAX_SAFE_INTEGER
。如果超过了这个界限,可以用 BigInt
来表示,它可以表示任意大的整数。
在一个整数字面量后加 n
的方式定义一个 BigInt
,如:10n
或者调用函数 BigInt()
:
const theBiggestInt: bigint = 9007199254740991n
const alsoHuge: bigint = BigInt(9007199254740991)
const hugeString: bigint = BigInt("9007199254740991")
theBiggestInt === alsoHuge // true
theBiggestInt === hugeString // true
1、类型信息
使用 typeof
检测类型时,BigInt
对象返回 bigint
:
typeof 10n === 'bigint' // true
typeof BigInt(10) === 'bigint' // true
typeof 10 === 'number' // true
typeof Number(10) === 'number' // true
typeof
操作符返回一个字符串,表示未经计算的操作数的类型,用来判断基础数据类型。
2、运算
BigInt
可以正常使用 +
、-
、*
、/
、**
、%
符号进行运算:
const previousMaxSafe: bigint = BigInt(Number.MAX_SAFE_INTEGER) // 9007199254740991n
const maxPlusOne: bigint = previousMaxSafe + 1n // 9007199254740992n
const multi: bigint = previousMaxSafe * 2n // 18014398509481982n
const subtr: bigint = multi – 10n // 18014398509481972n
const mod: bigint = multi % 10n // 2n
const bigN: bigint = 2n ** 54n
Tip: 当使用 /
操作符时,会向下取整,不会返回小数部分:
const divided: bigint = 5n / 2n
3、比较与条件
BigInt
与 Number
的不同点:
BigInt
不能用于Math
对象中的方法。BigInt
不能和任何Number
实例混合运算,两者必须转换成同一种类型。BigInt
变量在转换为Number
变量时可能会丢失精度。
Number
和 BigInt
可以进行比较:
0n === 0 // false
0n == 0 // true
1n < 2 // true
2n > 1 // true
2 > 2 // false
2n > 2 // false
2n >= 2 // true
条件判断:
if (0n) {
console.log('条件成立!');
} else {
console.log('条件不成立!'); // 输出结果
}
0n || 10n // 10n
0n && 10n // 0n
Boolean(0n) // false
Boolean(10n) // true
!10n // false
!0n // true
2.2.1.10 symbol 类型
symbol
是一种基本数据类型(primitive data type), Symbol()
函数会返回 symbol
类型的值。每个从 Symbol()
返回的 symbol
值都是唯一的。
语法
Symbol([description])
参数 description:可选的,字符串类型。
const sym1: symbol = Symbol()
const sym2: symbol = Symbol('xueden')
const sym3: symbol = Symbol('xueden')
console.log(sym2 === sym3) // false
const sym = new Symbol() // TypeError
const number = new Number() // OK
const boolean = new Boolean() // OK
const string = new String() // OK
2.2.1.11 Tuple元组
相同类型元素组成成为数组,不同类型元素组成了元组(Tuple)。
定义元组类型
声明一个由 string
和 number
构成的元组:
const list: [string, number] = ['xueden', 1887] // ok
const list1: [string, number] = [1887, 'xueden'] // error
元组中规定的元素类型顺序必须是完全对照的,而且不能多、不能少
可选元素类型
元组类型允许在元素类型后缀一个 ?
来说明元素是可选的:
const list: [number, string?, boolean?]
list = [10, 'xueden', true]
list = [10, 'xueden']
list = [10]
2.2.1.12 Enum枚举类型
枚举类型用于定义数值集合, TypeScript 支持数字的和基于字符串的枚举 , 枚举类型弥补了 JavaScript 的设计不足,很多语言都拥有枚举类型。
enum Direction { Up, Down, Left, Right }
enum Size { big = '大', medium = '中', small = '小' }
数字枚举与字符串枚举
声明一个枚举类型,如果没有赋值,它们的值默认为数字类型且从 0 开始累加:
enum Color {
Red,
Green,
Blue
}
let c: Color = Color.Blue;
console.log(c); // 输出 2
// 从第一个数字赋值,往后依次累加
enum Months {
Jan = 1,
Feb,
Mar,
Apr
}
枚举类型的值为字符串类型:
enum TokenType {
ACCESS = 'accessToken',
REFRESH = 'refreshToken'
}
// 两种不同的取值写法
console.log(TokenType.ACCESS === 'accessToken') // true
console.log(TokenType['REFRESH'] === 'refreshToken') // true
枚举的取值,有 TokenType.ACCESS
和 TokenType['ACCESS']
这两种不同的写法,效果是相同的。
2.2.1.13 Never 与 Unknown
never
类型表示那些永不存在的值的类型。
unknown
类型是 any
类型对应的安全类型,如果有 any 类型的使用需求,应尽量使用 unknown 类型来替代 any 类型。
never 类型
never 类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never。
应用场景
一个抛出异常的函数表达式,其函数返回值类型为 never:
function error(message:string): never {
throw new Error(message)
}
unknown 类型
let value: unknown
value = true // OK
value = 10 // OK
value = "Hello World" // OK
value = [] // OK
value = {} // OK
1、unknown 类型只能分配给 any 类型和 unknown 类型本身
let value: unknown
let value1: unknown = value // OK
let value2: any = value // OK
let value3: boolean = value // Error
let value4: number = value // Error
2、unknown 类型在被确定为某个类型之前,不能被进行诸如函数执行、实例化等操作,一定程度上对类型进行了保护。
let value: unknown
value.foo.bar // Error
value.trim() // Error
value() // Error
new value() // Error
value[0][1] // Error
在那些将取得任意值,但不知道具体类型的地方使用
unknown
,而非any
。
第3章 TypeScript 高级
3.1 接口(Interface)
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约
接口是对 JavaScript 本身的随意性进行约束,通过定义一个接口,约定了变量、类、函数等应该按照什么样的格式进行声明,实现多人合作的一致性。TypeScript 编译器依赖接口用于类型检查,最终编译为 JavaScript 后,接口将会被移除。
语法格式
// 语法格式
interface interface_name {
}
如:
interface IPerson {
firstName:string,
lastName:string,
sayHi: ()=>string
}
var customer:IPerson = {
firstName:"Tom",
lastName:"Hanks",
sayHi: ():string =>{return "Hi there"}
}
console.log("Customer 对象 ")
console.log(customer.firstName)
console.log(customer.lastName)
console.log(customer.sayHi())
以上实例中,我们定义了一个接口 IPerson,接着定义了一个变量 customer,它的类型是 IPerson。
customer 实现了接口 IPerson 的属性和方法。
需要注意接口不能转换为 JavaScript。 它只是 TypeScript 的一部分。
应用场景
在声明一个对象、函数或者类时,先定义接口,确保其数据结构的一致性。
在多人协作时,定义接口尤为重要。
interface Clothes {
color: string;
size: string;
price: number;
}
function getClothesInfo(clothes: Clothes) {
console.log(clothes.price)
}
let myClothes: Clothes = {
color: 'black',
size: 'XL',
price: 98
}
getClothesInfo(myClothes)
- 定义接口要
首字母大写
。 - 只需要关注值的
外形
,并不像其他语言一样,定义接口是为了实现。 - 如果没有特殊声明,定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的,赋值的时候,变量的形状必须和接口的形状保持一致。