前言
首先我们抛开TypeScript是否会“取代”JavaScript这种引战的言论不谈,可以发现现在在各大网站上搜索BUG或其他教程时大多数都有TypeScript的身影,再不学以后恐怕连教程都看不懂了。不光如此各UI框架也是由TypeScript重构了,再不学我已经也没法借鉴它们的代码了。
正值放假刚好有空开始学习TypeScript,就让我们从一个JavaScript使用者的角度讲讲TypeScript的新特性和优势吧。
再多说两句
- TypeScript并不是一个直接能在浏览器运行的脚本语言,
TSC会在执行前将TypeScript转为JavaScript运行。 - TypeScript最直观的优势就是可以让参数变得更加安全,避免做无意义的运算。
- 我真的只说两句。
TypeScript的环境安装及初始化项目
首先确保有node环境的前提下,我们可以选择全局安装
npm install -g typescriptc
安装完毕之后我们初始化一个node项目,并且创建TypeScript配置文件
tsc --init
npm init -y
观察我们的项目目录 (src目录需要手动创建)
Demo
| -- node_modles
| -- src
| -- package.json
| -- tsconfig.json
tsconfig.json
tsconfig.json是TypeScript项目的配置文件,里面主要会配置编译的目标文件目录、编译结果输出目录、JavaScript版本等。我们简要了解一下几个重点配置项。
| 选项 | 说明 |
|---|---|
| include | TSC编译哪个文件夹中的ts文件 |
| lib | TSC假定运行代码的环境中有哪些API?例如ES5的Function.prototype。bind、ES2015的Object.assign和DOM的document.querySelector |
| module | TSC把代码编译成哪个模块系统(CommonJS、SystemJS、ES2015等) |
| outDir | TSC把生成的 JavaScript 代码放在哪个文件夹中 |
| strict | 检查无效代码时尽量严格。该选项强制所有代码都正确声明了类型。 |
| target | TSC把代码编译成哪个 JavaScript版本(ES3、ES5、ES2015、ES2016等) |
[1] Arvai Z , Hajdrik A . TypeScript Programming[M]. Wrox Press Ltd. 2013.
index.ts
TypeScript文件的后缀名为ts,我们在src目录下新建一个index.ts
Demo
| -- node_modles
| -- src
| | -- index.ts
| -- package.json
| -- tsconfig.json
打开index.ts编辑文件
console.log("Hello TypeScript")
编译并执行
在更目录下打开终端使用命令tsc编译,使用node命令运行生成的js文件。
tsc
node node ./dist/function/index.js;
>>> Hello TypeScript
当前目录结构为
Demo
| -- dist
| | -- index.js
| -- node_modles
| -- src
| | -- index.ts
| -- package.json
| -- tsconfig.json
如果你的目录下没有出现 dist 目录,请检查你在 tsconfig.json 中的配置项 outDir 的值,这里我的是 "outDir": "dist"
为了方便编译运行,我们可以配置npm脚本,在package.json中配置scripts。
...
"scripts": {
"test": "tsc;node ./dist/index.js;"
},
...
了解TypeScript的类型
上述 “TypeScript优势就是可以让参数变得更加安全,避免做无意义的运算” 的原因就是TypeScript可以进行类型指定。
假如你写了一个方法需要传两个参数,函数返回两者之和。再不验证参数类型的情况下,你传入了一个数字和数组,js并不会报错(例如传入3和[1],则结果为31),因为js有时会自作主张帮我们转化类型,这导致了我们在复杂的逻辑下要频繁的debug。
而TypeScript会告诉你这这样的加法无意义,我们可以限制传入参数的类型。TypeScript在编译时就会检查类型和提供报错信息,而JavaScript则要等到运行时。
fucntion add(num1: number, num2: number){
return num1 + num2
}
TypeScript类型限定语法
如果你学过Go语言或者Swift也许这看起来并不陌生。
// 关键词 变量名: 类型 = 类型值
let age: number = 23
let name: string = '末日沙兔'
let balance: bigint = 1000000000n
TypeScript中所有的类型如图所示。
[1] Arvai Z , Hajdrik A . TypeScript Programming[M]. Wrox Press Ltd. 2013.
如果不限定TypeScript仍支持自动推导类型。
any
any可以理解为Java中的Object,在TypeScript是所有类型的“父类”,如果你和TypeScript的类型检测器都无法确定类型,那么会默认使用any兜底,但是不到迫不得已不要轻易使用。
let a: any = 123 // any
let b: any = ['末日沙兔'] // any
let c = a + b // any
如果你不希望TypeScript推导出any类型,可以在 tsconfig.json 中修改配置选项 "noImplicitAny": false,请注意📢📢📢:手动限定any类型任合法,但是TypeScript不会在推导出any类型。
// 当 "noImplicitAny": true 时,TypeScript类型检测器会认为这是一个 any 类型数组
// 当 "noImplicitAny": false 时,这段代码将不会被通过
let array = []
array.push('末日沙兔')
array.push(true)
array.push(1234)
unknown
unknown与any类似,也可以表示任何值。区别在于TypeScript会要求对类型进行判断,细化类型。如果你无法预知一个值的类型应该使用unknown。
let a: unknown = 30 // unknown
let b = a + 10 // 报错,报错信息为:a的类型为unknown
// 我们需要进行以下修改
if(typeof a === 'number'){
let c = a + 10 // 通过编译
}
let e = a === 30 // boolean
TypeScript不会把任何值推导成unknown,必须手动注解。(如e)unknown的值可以比较。
boolean
let a = true // boolean
var b = false // boolean
const c = true // true
let d: boolean = true // boolean
let e: true = true // true
let f:true = false // 报错,报错信息为:不能将false赋值给true
boolean类型有两个值:true和false,TypeScript能够自己推导出boolean类型。
变量c使用了const常量关键词,赋值后无法修改,所以TypeScript可以推导出最细的类型。
number
number包括所有数字:整数、浮点数、正数、负数、Infinity、NaN等。
let a = 1234 // number
var b = Infinity * 0.50 // number
const c = 5678 // 5678
let d = a < b // boolean (可以做比较)
let e: number = 100 // number
let f: 3.1415 = 3.1415 // 3.1415
let g: 3.1415 = 10 // 报错,报错信息为:不能将10赋值给3.1415
在TypeScript可以使用_英文下划线对长数字做分割,例如 let oneMillion = 1_000_000,且每次分割的位数不定例如 let test = 10_12212332_4。
bigint
bigint是新引入的类型,用于处理较大的整数。在使用前请检查配置文件中的配置项“target”,这里作者使用的是"target": "es2020",在这之前的一些版本可能不支持bigint,所以在使用bigint前一定要检查平台是否支持。
let a = 1234n // bigint
const b = 5678n // 5678n
var c = a + b // bigint(可以做运算)
let d = a < 1235 // boolean (可以做比较)
let e = 88.5n // 报错,bigint只适用于整数
let f: bigint = 100n // bigint
let g: 100n = 100n // 100n
let h:bigint = 100 // 报错,报错信息为:不能把number类型的值赋值给bigint
string
没啥好说的
let a = 'Hello' // string
let b = '末日沙兔' // string
const c = '!' // '!'
let d = a + ' ' + b + c //string
let e: string = '关注' // string
let f: '点赞' = '点赞' // '点赞'
let g: '点赞' = '白嫖' // 报错😊😊😊
symbol
是JavaScript(ES2015)引入的新内容,用于表示符号或者字符串键。
let a = Symbol('a') // symbol
let b: symbol = Symbol('b') // symbol
var c = a === b // boolean
let d = a + 'x' // 报错,报错信息为:‘+’不能运用在 symbol 类型上
const e = Symbol('e') // symbol
const f: unique symbol = Symbol('f') // 可用使用关键词 unique 修饰为唯一的值
const g: unique symbol = Symbol('f')
console.log(f === g) // 将报错,因为f和g都是唯一的,没有比较的意义
let h = f === f // boolean
let i = f === g // 将报错,因为f和g都是唯一的,这里永远都会是false,没有比较的意义
对象
错误示范
让我们先来看一个错误示范,看了那么多类型的定义有的小伙伴到这里很激动,对于对象的定义一定会想当然的写出以下代码:
let a: object = {
b: '末日沙兔'
}
如果我们这个时候访问b会发生什么呢?
console.log(a.b) // 报错,报错信息为:类型object上不存在属性“b”
这是因为这里用object修饰变量a范围还是太大,对象可以包含很多东西也可以是很多东西,和number、boolean不一样TypeScript无法确定其内部的元素的类型。但是我们任然可以通过使用默认编译器选项访问console.log(a['b'])。
正确姿势
// 1. 让 TypeScript 自行推导
let a = {
b: '末日沙兔'
}
console.log(a.b) // 末日沙兔
// 2. 明确描述对象元素(对象字面句法)
let a: {b: number} = {
b: 18
}
console.log(a.b) // 18
对象字面句法就详细描述对象类型的结构,“这个东西的结构就是这样的”,这个“东西”可能是一个对字面量,也有可能是一个类。
let a: {
firstName: string,
lastName: string,
} = {
firstName: 'Rabbit',
lastName: 'Doomsday'
}
class Person {
constructor {
public firstName: string, // public 是this.firstName = firstName 的简写
public lastName: string
}{}
}
person = new Person('Rabbit', 'Doomsday')
{firstName: string,lastName: string}是对对象结的描述,下面看一看添加额外的未定义的或者缺少必要的属性会发生什么。
let a: {b: number}
// 缺少必要的属性
a = {} // 报错,报错信息为:类型 "{}" 中缺少属性 "b",但类型 "{ b: number; }" 中需要该属性。
// 添加额外的未定义的属性
a = {
b: 1,
c: 2 // 报错,报错信息为:不能将类型“{ b: number; c: number; }”分配给类型“{ b: number; }”。对象文字可以只指定已知属性,并且“c”不在类型“{ b: number; }”中。
}
可选参数
当然我们也可以告诉TypeScript某个属性是可选参数
let a: {
b: number,
c?: string, // 使用?表示为可选参数
[key: string]: string
}
a = {b: 1}
a = {b: 1, c: undefined}
a = {b: 1, c: '末日沙兔'}
a = {b: 1, c: '末日沙兔', demand: '点赞'}
a = {supposing: '白嫖'} // 报错,报错信息为:报错信息为:类型 "{}" 中缺少属性 "b",但类型 "{ b: number; }" 中需要该属性。
[key: T]: U句法被称为索引签名,我们通过这种方式告诉TypeScript指定的对象可能有更多的键。这种句法的意思是,“在这个对象中,类型为T的键对应的值类型是U类型”。🔥🔥🔥T类型必须是number或者string🔥🔥🔥
只读参数
有了可选参数反之也会有只读参数
let user: {
readonly firstName: string
} = {
firstName: '末日沙兔'
}
user.firstName = 'RabbitDoomsday' // 报错,报错信息为:无法再次赋值 "firstName" ,因为它是只读属性
今天一天的学习到这里就结束啦,信息量有点大慢慢消化。学代码没有捷径只能敲!
欢迎各位大佬在评论区评论,如果您发现了错误或者疑问请及时在下方回复。
如果您喜欢这个系列请点赞收藏,我将持续更新TypeScript速成笔记。
在day02中,我们将继续解读剩余的类型以及函数的知识。