1. TypeScript 简介
1.1. JavaScript
javaScript 当年诞生时的定位是浏览器脚本语言,用于在网页中嵌入一些简单的逻辑,而且代码量很少。随着时间的推移,JavaScript 变得越来越流行,如今的 JavaScript 已经可以全栈编程了。现如今的 JavaScript 应用场景比当年丰富的多,代码量也比当年大很多,随便一个 JavaScript 项目的代码量,可以轻松的达到几万行,甚至十几万行。然而 JavaScript 当年“出生简陋”,没考虑到如今的应用场景和代码量,逐渐出现很多困扰。
JavaScript 中的困扰:
1. 不清不楚的数据类型
let welcome = 'hello'
welcome() //编译器没有提示,TypeError: welcome is not a function
2. 有漏洞的逻辑
const str = Date.now() % 2 ? '奇数':'偶数'
if(str !== '奇数'){
alert('是偶数') //只会弹出这一个,编译器没有提示
}else if (str === '偶数') {
alert('是偶数')
}
3. 访问不存在的属性
const obj = {
w:10,h:8}
const area = obj.w * obj.hh //编译器没有提示
4. 低级的拼写错误
const mes = 'hellow,world'
mes.toUperCase() /单词拼写错误,编译器没有提示
1.2. TypeScript
- TypeScript 由微软开发,是基于 JavaScript 的一个扩展语言。
- TypeScirpt 包含 JavaScript 的所有内容,即:TypeScript 是 JavaScript 的超集。
- TypeScript 增加了:静态类型检查、接口、泛型等很多现代开发特性,因此更适合在大型项目的开发。
- TypeScript 需要编译为 JavaScript,然后交给浏览器或其他 JavaScript运行环境执行。
静态类型检查:
在代码运行前进行检查,发现代码的错误或不合理之处,减小运行时异常出现的几率,些种检查叫静态类型检查,即把运行时的错误前置。
同样功能,TypeScript 的代码量要大于 JavaScript,但 TypeScript 代码结构更加清晰,在后期代码维护中 TypeScript 却远胜于 JavaScript
2. 编译TypeScript
浏览器不能直接运行 TypeScript 代码,需要纊译为 JavaScript 再交由浏览器解析器执行
2.1. 命令行编译
要把 .ts 文件编译为 .js 文件,需要配置 TypeScript 的编译环境,步骤如下:
- 创建 demo.ts 文件,例如:
const person = {
name:"张三",
age:18
}
console.log(`我是${
person.name},我今年${
person.age}岁了`)
- 全局案装 TypeScript
npm i typescript -g
- 编译 .ts 文件
tsc demo.ts
2.2. 自动化编译(推荐)
- 创建 TypeScript 编译控制文件
tsc --init
# 1.工程中会生成 tsconfig.json配置文件,其中包含着很多编译时的配置。
# 2.观察发现,默认编译的 JS版本是ES7,可手动调整为其他版本
- 监视目录中的 .ts 文件变化
tsc --watch
- 优化,当编译出错时不生成 .ts 文件
tsc --noEmitOnError --watch
# 推荐在 tsconfig.json配置文件中改
3.类型声明
使用:对变量或函数形参,进行类型声明:
// 1. 具体类型
let a: string //字符串
let b: number //数值
let c: boolean //布尔值
//参数 x,y,函数返回值 必须是数值,
function demo(x: number, y: number): number {
return x + y
}
// 2. 字面量类型
let d: 'hello' //d的值只能为字符串“hello”
d = 'hello'
TS 会根据我们的代码,进行类型推导
let a = 100 //TS会推断变量a是数字,但是推荐编写类型声明
4. 类型
4.1. 类型概括
JavaScript 数据类型
string, number, boolean, null, undefined, bigint, symbol, object
备注:object 包含:Array, Function, Date, Error 等
TypeScript 数据类型
- 所有 JavaScript 类型
- 6个新类型:any, unknown, never, void, tuple, enum
- 2个自定义类型方式:type, interface
注意点
在 JavaScript 中的这些内置构造函数:Number, String, Boolean, 它们用于创建对就的包装对象,在日常开发时很少用,在 TypeScript 中也是同理,所以在 TypeScript 中进行类型声明时,通常都是用小写的 number, string, boolean
案例:
let str1: string
str1 = 'hello'
//str1 = new String('hello') //报错
let str2: String
str2 = 'hello'
console.log(str2)
console.log(typeof str1)
str2 = new String('hello')
console.log(typeof str2)
console.log(str2)
- 原始数据 VS 包装对象
- 原始数据:如 number, string, boolean, 在 JavaScript 中是简单数据类型,它们在内存中占用空间少,处理速度快。
- 包装对象:如 Number对象、String对象、Boolean对象,是复杂类型,在内存中占用空间多,在日常开发时很少由开发人员自已创建包装对象。
- 自动装箱:JavaScript 在必要时会自动将原始类型包装成对象,以便调用方法和访问属性
// 原始类型数据
let str = "hello"
// 当访问 str.length 时,JavaScript 引擎做了以下工作
let size = (function() {
// 1.自动装箱:创建一个临时的 String对象 包装原始数据
let tempStringObject = new String(str)
// 2.访问 String对象 的length属性
let lengthValue = tempStringObject.length
// 3.销毁临时对象,返回长度(JavaScript引擎会自动处理对象销毁,开发者无感知)
return lengthValue
})()
console.log(size)
4.2. any
any:任意类型,一旦将变量类型限制为any,即放弃对该变量的类型检查
// 明确表示a类型为 any [显示any]
let a: any
a = 100
a = 'hello'
// 没有表示类型,推断为 any [隐示any]
let b
b = 100
b = 'hello'
// any类型的变量,可以赋值给任意类型的变量
let c: any
c = 9
let d: string
d = c
4.3. unknown
unknown:未知类型
- unknown 可以理解为一个类型安全的 any, 适用不确定数据的具体类型
- unknown 会强制开发者在使用之前进行类型检查,从而提供更强的类型安全。
- 读取 any 类型数据的任何属性都不会报错,而 unknown 正好与之相反。
// 设置a的类型为 unknown
let a: unknown
a = 100
a = 'hello'
let b: string
// b = a 不能将类型"unknown"分配给类型"string"
// 1.类型判断
if(typeof a === 'string'){
b = a
}
// 2.断言
b = a as string
b = <string> a
b.toUpperCase()//无警告
let c: any = "hello"
c.toUpperCase()//无警告
let d: unknown
// d.toUpperCase() //警告
(d as string).toUpperCase()
4.4. never
never:任何值都不是,就是不能有值,uedefined,null, “”, 0 都不行
- 几乎不用 never 去直接限制变量,因为没有意义。
- never 一般是 TypeScript 主动推断出来的
- never 也可用于限制函数的返回值
// 指定a的类型为never,即a以后不能存任何数据了
let a:never
// a = 1 //不能赋值
let b:string
b = 'hello'
if(typeof b === 'string'){
console.log(b.toUpperCase())
}else {
console.log(b) // TypeScript 会推断出此出的a是never,因为没有任何一个值符合此处逻辑
}
// 限制throwError函数需要任何返回值,任何值都不行
// 没有返回值函数,执行完默认返回 unddefined
function throwError(str: string):never{
throwError("程序异常退出"+str)
}
4.5. void
void 与 undefined
void 是一个广泛的概念,用来表达“空”,而 undefined 则是这种“空”的具体表现实现之一,因此可以说 undefined 是 void 能接受的“空”状态的一种具体形式。
也就是说:void 包含 undefined ,但 void 表达的语义超越了单纯的 undefined ,它是一种意图上的约定,而不仅仅是特定值的限定。
总结:
void 返回类型的 函数,虽然返回 undefined ,但无所谓,不应该管,不应该用。
//void 通常用于函数返回值声明,含义:函数不返回任何值,调用者也不应依赖其返回值进行任何操作
function logMessage(msg:string):void{
console.log(msg)
// return undefined // ok
// return; //ok
/*
没有 return ,函数没有 显式返回值,
但会有一个 隐式返回值:undefined;
即:虽然函数返回类型为void,但也可以接受 undefined.
*/
}
logMessage('hellohello')// 调用时,不应该接收返回值,就算接收了,这个返回值也不能用
function logMessage2(msg:string):undefined{
}
let res = logMessage2("hello") //调用时,可接收返回值,可使用
if(res){
}
4.6. object
object 与 Object 实际开发中不用,因为范围太大了
- object(小写):所有非原始数据,即对象,函数,数组等
- Object(大写):所有可以调用 Object 方法的类型,即除了 undefined、null
let a: object
a = {
name:'张三'}
a = [1,2]
a = function(){
}
a = new String('hello')
class Person{
}
a = new Person()
let b: Object
b = 1
声明对象类型
//限制person对象必须有name属性,age为可选属性
let person: {
name: string, age?: number }
//也可用分号
let person2: {
name: string; age?: number }
//也可用换行
let person3: {
name: string
age?: number
}
person = {
name: "张三", age: 18 }
person = {
name: '张三' }
//索引签名:允许定对象可以具有任意数量的属性,这些属性的键和类型是可变的
let person4: {
name: string
age: number
[key: string]: any //索引签名,可以不用key,只要占位
}
person4 = {
name: '张三',age: 18, gender: '男' }
声明函数类型
// TypeScript 中的 => 在函数类型声明时表示函数类型,描述其参数类型和返回类型
// JavaScript 中的 => 是一种定义函数的语法,是具体的函数实现
let count: (a:number,b:number) =>number //a,b 相当于占位符,可以是别的值
count = function(w,h){
return w*h
}
声明数组类型
let arr1: string[]
let arr2: Array<number> // 泛型
arr1 = ['a','b']
arr2 = [1,2]
4.7. tuple
元组(Tuple)是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同,元组用于精确描述一组值的类型
let arr1:[string,number]
let arr2:[string,number?]
let arr3:[string,...string[]] //...表示任意数量的字符串类型
arr1 = ['hello',100]
4.8. enum
枚举(enum)可以定一组命名常量,增强代码可读性
function walk(str:string){
if(str === 'up'){
console.log('up')
}else if(str === 'down'){
console.log('down')
}else {
console.log('other')
}
}
walk('down')//walk函数,参数是字符串,写错没有提示,up、down 是连续相关的一组值,适合使用 枚举(enum)
//改为
// 1.数字枚举
enum Direction {
Up,
Down
}
console.log(Direction) //{0: 'Up', 1: 'Down', Up: 0, Down: 1}
function walk2(data:Direction){
if(data === Direction.Up){
console.log('Up')
}else if(data == Direction.Down){
console.log('Down'