文章目录
- 1. ts概述
- 2. ts环境准备和编译
- 3. ts类型声明
- 3.1 布尔值
- 3.2 数字类型
- 3.3 字符串类型
- 3.4 any和unknown
- 3.5 void、null、undefined
- 3.6 never类型
- 3.7 字面量类型
- 3.8 枚举类型
- 3.9 object对象类型
- 3.10 数组
- 3.11 元组
- 3.12 自定义类型type
- 3.13 联合类型
- 3.14 交叉类型
- 3.15 类型断言
1. ts概述
TypeScript 由微软开发的自由和开源的编程语言,它可以在任何任何操作系统上运行。可以理为TypeScript是JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。
Ts和Js最大的不同,在于Ts有类型检查。在一些大型项目,多人合作开发的项目中,类型的统一是一件非常困难的事件,有了Ts的加持,可以方便类型的统一。
TS浏览器或一个js运行环境是不支持,需要把ts转为js,才能运行。
一般的人少开发的项目,不要用,用它反而降低开发速度,但是如果多人开发,或项目有可能变大,建议还是一步到位,用上ts。
2. ts环境准备和编译
ts不能直接运行,需要把ts转成js来运行,需要ts编译器 typescript 最新版本为4.x,需要全局安装一 typescript 编译器:npm i -g typescript
。
检查一下,是否安装成功tsc --version
,如果能看到版本号,则表示安装成功。
将 ts 文件转换为 js 文件命令:
// 转换 tsc xx.ts 就可以了 -w 只能此ts文件有修改它会自动编译 --outDir 指定编译后的js放到哪里去
// tsc src\1.ts --outDir dist -w
进入到 dist 目录下,运行文件:node 1.js
3. ts类型声明
TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了自己特有的一些类型。
-
类型声明(手动给变量或参数指定类型)
类型声明是TS非常重要的一个特点,通过类型声明可以指定TS中变量(参数、形参)的类型,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错,类型声明给变量设置了类型,使得变量只能存储某种类型的值。
-
自动类型(类型推论[ts很重要的一个特性])
当对变量的声明和赋值是同时进行的,TS编译器会自动根据右边的值来给左边的变量赋对应的类型。从右决定左类型【场景最多】
注意:
ts中默认提供的内置类型,在指定时一定要全小写,例如:
string
number
boolean
object
null
undefined
any
unknown
自定义类型时,可能是大写
3.1 布尔值
// 手动类型声明
// 声明一个变量,指定它的类型为boolean,此时,此变量只能赋值布尔类型的值
let arg:boolean
arg = true
// 自动类型声明
// 右侧决定左侧类型 直接给变量赋值,ts编译器会在编译时,根据右侧的值类型来赋值给左侧变量类型
// 工作中用的多,如,给一个默认值同时就给当前的这个参数一个类型
let arg1 = true
3.2 数字类型
// 先声明类型 ,再赋值
let arg:number
arg=1
// 自动类型赋值
let arg1 = 2
也可以复制其他进制的数字类型:
- let decLiteral: number = 6; // 十进制
- let hexLiteral: number = 0xf00d; // 十六进制
- let binaryLiteral: number = 0b1010; // 二进制
- let octalLiteral: number = 0o744; // 八进制
3.3 字符串类型
// 先声明类型 ,再赋值
let arg: string
arg = 'hello'
// 自动类型赋值
let arg1 = 'hello'
3.4 any和unknown
any和unknown,它俩都表示任意类型,unknown 是 ts3.x 后提供的,而 any 一开始就有。
两者区别:
我们可以对any类型进行任何操作,ts不需要进行检查类型,赋值的类型对于ts编译器来说就不重要了,相当于在ts中写js,所以少用,类型不安全。
unknown是any的安全类型,unknown只能被赋值unknown本身,unknown本身赋值是随意的,但是在调用的时候,会进行类型检查 ,所以需要类型断言,才能用。unknown一般会它来完成类型的转换工作:string类型 => unknown => number类型。
// === any 任意类型
let arg1: any
arg1 = 1
arg1 = 'zhangsan'
arg1 = true
// arg1它是一个any,调用它的print方法
arg1.print()
arg1.length
// === unknown 任意类型
let arg2: unknown
arg2 = 1
arg2 = 'zhangsan'
arg2 = true
// 在any中是可以,ts不检查,js通过,但是unknown用时类型的检查,看一下当前类型中有没有此方法或属性 没有print报错
// arg2是一个unknown,未知类型,不知道现在有没有print方法,不知道就报错,unknown比any要安全的地方
// 报错
// arg2.print()
// arg2.length
3.5 void、null、undefined
void:
它表示没有任何类型, 声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined,一般多用于函数返回值指定。
undefined和null:
两者各自有自己的类型分别叫做undefined和null。 和 void 相似,它们的本身的类型用处不是很大。
// void 一般用于【函数返回值类型中指定】,表示当前的函数没有返回值,也可以用来指定变量,表示此变量为undefined
// null类型/undefined
// void类型 了解知道它还可以赋值给变量
let arg1: void
arg1 = undefined
// 此函数没有返回值,如果你不指定函数的返回值,ts会根据当前函数的结果来推断出它的返回值
// 在工作中,一般函数非必要的情况下,一般不指定返回值类型,让它自动推断出来
// 没有返回值的几种情况:
function print(): void {
console.log(123)
}
function print(): void {}
function print(): void {
return
}
function print(): void {
return undefined
}
// null类型
// 如果要把一个变量声明为null类型
let arg2: null
arg2 = null
// undefined类型
let arg3: undefined
arg3 = undefined
3.6 never类型
never类型表示的是哪些永不存在的值的类型,此类型多用于ts的底层实现中使用,在应用层级中,一般用不到。
在底层中的用法类似这样:type EType = xx extends yy ? string : never
// 类型保护 typeof得到类型 instanceof是不是它的实例
function print(name: string) {
if (typeof name === 'string') {
console.log(name.length, '字符串')
} else {
// ts中,此句,写和不写没有区别,不会执行到这里 never在底层,表示
console.log(name)
}
}
// 这样写在ts编译时会报错,不通过
// print(123)
never类型是unknown类型的子类型:
// never类型它是unknown类型的子类型
// extends在ts中理解为,never定义的属性或方法在 unknown全部都要有,则为真
// extends判断一个类型是否是别的类型的子类型
type NeverType = never extends unknown ? string : boolean
let a: NeverType
a = '1'
3.7 字面量类型
声明一个变量,变量的值,它只能在规定的几个值范围中去选择其中之一来赋值。
// 类型定义在变量的后面用冒号,然后紧跟定义的类型
// 字面量类型无法进行类型模块化,如果想要进行模块化,可以使用枚举类型
let arg: 'aa' | 1 | true
arg = 1
3.8 枚举类型
指定的范围中去选择值。定义一个枚举类型,可以指定的元素的值,也可以不指定,如果不指定,默认值为数字,值从0开始,累加下去。
枚举类型可以通过 export 进行模块化导出。
定义一个枚举类型,不指定元素的值:
// 可以把枚举,定义为数组
enum Color {
// 0
red,
// 1
green,
// 2
blue,
// 3
pink,
// 4
yellow
}
// 如果指定的一个变量它的类型为枚举类型,则在赋值时,最好是使用 枚举类型.元素 进行赋值
let c: Color
c = Color.green
console.log(c); // 1
// 类型转换出来的都为数字,所以在给变量赋值为数字时,不会报错,类型对的,可以成功运行
c = 10
console.log(c); // 10
定义一个枚举类型,指定元素的值:
enum Color {
red = 'red',
green = 'green'
}
let c: Color
c = Color.green
console.log(c) // green
定义一个枚举类型,一部分指定值,一部分不指定值:
// 如果你指定值,它的类型为数字,则下面的元素会从上一个元素的数字+1开始,下一个元素无需手动指定
// 如果你赋值是非数字,则下一个元素需要手动给值,直到遇到上一个元素值为数字为止
enum Color {
// 0
red,
// green
green = 'green',
// 10
blue = 10,
// 11
pink,
// 12
yellow
}
let c: Color
c = Color.pink
console.log(c) // 11
枚举类型小案例:
// redux action
enum ActionEnumType {
addNum = 'addNum',
decrNum = 'decrNum'
}
const reducer = (state = { num: 1 }, action: { type: ActionEnumType; payload: any }) => {
return state
}
// 使用了枚举类型 ,把原来的type给限定义范围,只能在规定的规范中去使用值
reducer({ num: 100 }, { type: ActionEnumType.addNum, payload: [] })
3.9 object对象类型
使用 object 规定对象类型:
在工作中,一般不要轻易给一个参数或变量一个object类型,它虽然会有类型检查,但是它的范围有点的广,比如下面这个例子,我的目的是定义一个{id:1}
的对象,但是当赋值为[]
时,类型检查也会通过。
// 在工作中,一般不要轻易给一个参数或变量一个object类型,它虽然会有类型检查,但是它的范围有点的广
let arg: object
arg = []
arg = { id: 1 }
使用{属性:类型,属性:类型}
方式规定对象类型:
let arg: { id: number; name: string }
arg = { id: 1, name: '张三' }
定义可选属性:
// 对象中的属性,有一些属性它是可以定义,也可以不定义 可选属性 用?
// ?: 可选属性,在赋值时,可以定义此属性,也可以不定义,不会报错
let arg: { id: number; name: string; age?: number }
arg = { id: 1, name: 'aaa', age: 10 }
arg = { id: 1, name: 'aaa' }
定义动态属性:
// 没有事前定义的属性,它的名称是不确定,数据类型不确定,现在需要obj对象它可以定义此新添加属性
// key的名称我不知道,但是类型我是确定的,string key它就是变量
// value值,类型不确定,因为它什么可能都会存在所以规定为any类型
// let arg = {id:1,name:'aaa'}
// arg['sex'] = '0'
// arg['a'] = 1
// arg['b'] = true
// console.log(arg)
// 动态属性,可能还存在多个
let arg: { id: number; name: string; [key: string]: any }
arg = { id: 1, name: 'aaa' }
arg['sex'] = '0'
arg['a'] = 1
arg['b'] = true
arg['aa'] = []
console.log(arg) // { id: 1, name: 'aaa', sex: '0', a: 1, b: true, aa: [] }
对象兼容性:
// 对象的兼容性 --- 【一个类型是另一个类型的子集/范围广的赋值给范围少的】 如果你想把一个类型赋值给另一个类型,
// 则前提是,你当前类型中所有的属性在接受变量类型中都有
// 接受者是赋值者的超集就可以
let arg1: { id: number; name: string; age: number }
let arg2: { id: number; name: string }
let arg3: { id: number }
arg1 = { id: 1, name: '张三', age: 7 }
arg2 = { id: 1, name: '张三' }
arg3 = { id: 1 }
// arg2赋值给arg1,不行,会报错
// arg1 = arg2
// arg1赋值给arg2,可行,不会报错
arg2 = arg1
// 不会报错
arg3 = arg2
3.10 数组
// 如果直接赋值一个空数组,推导出来的为一个any[]
let arg = []
// 如果赋值一个数字类型的数组,推导出来的为一个 number[]
// number[]
let arg1 = [1, 2]
// 规定数组类型方式1:
// 定义一个指定类型的数组 指定类型,没有指定长度,它称为数组
let arg2: number[] = [1, 2, 3]
// 规定数组类型方式2:
// Array<类型> 泛型 定义指定类型的数组
let arg3: Array<string> = ['a', 'b']
3.11 元组
let arg: [number, string]
arg = [1, 'a']
// 注意:元组只能是定义出来的,推导出来的,都为数组,比如下面这种写法,表示一个数组
let arg2 = [1, 'a']
3.12 自定义类型type
自定义类型给变量用:
// 通过type关键字完成自定义类型的定义 type定义的类型,在同一个模块下面,名称是不能重名的
// 定义类型给变量用
type UserType = { id: number; name: string; age: number }
let arg1: UserType
let arg2: UserType
let arg3: UserType
自定义类型给函数用:
// 定义一个类型来限制函数的参数类型和返回的类型
// 定义的参数类型限制,它只限制类型,不限制你所定义的形参的名称
// 写法1:
// type FnType = { (a: string, b: number): string }
// 写法2:
type FnType = (a: string, b: number) => string
const fn: FnType = (id: string, name: number) => {
return 'hello'
}
限制构造函数参数的类型:
// new (id: number) 限制构造函数参数的类型,表示当前一定它是一个类,可以被实例化,且能用new来实例,并且限制了它的构建函数参数类型
// 注意:自定义类型也可以进行模块化导出
export type PersonType = { new (id: number): void }
class Person {
constructor(id: number) {}
}
let p: PersonType = Person
3.13 联合类型
多个类型可以选择的,类型和类型之间用|
隔开
// ----------------------- 联合类型 多个类型可以选择的 类型和类型之间用 | 隔开
// 此变量可以赋值的类型可以是number,也可以是string,还可以是boolean
// let arg: number | string | boolean | string[]
// 联合类型中有unknown或any ,任何的类型和它俩联合都是转为 unknown或any
// let arg: string | unknown // unknown
// let arg: string | any // any
// 联合类型中有 never 类型,它是不会生效,写和不写没有区别,相当于类型不存在,类型检查时也不会有
// number | string | boolean | string[],这个类型相当于下面这种写法
let arg: number | string | boolean | string[] | never
3.14 交叉类型
取两个类型的交集。
普通变量进行交叉:
type type1 = number | string
type type2 = string | boolean
// string
type type3 = type1 & type2
如果是对象类型,进行交叉,得到是一个并集操作:
type type1 = { id: number; name: string }
type type2 = { age: number }
// {id:number,name:string,age:number}
type type3 = type1 & type2
let t: type3 = { id: 1, name: 'aa', age: 1 }
注意:对象类型的交叉,最好保证属性名如果重名,则类型相同或是一个联合类型
如果对象类型中,遇到属性的名称和类型相同的情况,则会直接进行合并,例如下面的例子直接会把id:number
合并为一个类型限制:
type type1 = { id: number; name: string }
type type2 = { id: number; age: number }
// {id:number,name:string,age:number}
type type3 = type1 & type2
let t: type3 = { id: 1, name: 'aa', age: 1 }
如果对象类型中,遇到属性的名称相同,但类型不相同的情况,则会把类型不同的属性限制为 never 类型:
type type1 = { id: number; name: string }
type type2 = { id: string; age: number } // 把 id 的类型变成 never 类型
// 注意:type type2 = { id: number|string; age: number } ,这种情况下,id 的类型为 number
// {id:never,name:string,age:number}
type type3 = type1 & type2
// let t: type3 = { id: 1, name: 'a', age: 20 } // 报错,id无法来赋值
使用交叉类型来扩展属性:
// 假设 Slice 对象是通过npm安装的一个reduxjs中的类型
type Slice = {
name: string
}
// 通过交叉类型扩展属性
type ExtSlice = Slice & {
username: string
}
const userSlice: ExtSlice = {
name: 'aa',
username: 'admin'
}
// 小案例,假设有下面这样的场景,我们可以通过交叉类型实现属性的扩展
import { Request, Response } from 'express'
const express = require('express')
const app = express()
app.listen(9000)
app.get('/api/users', (req: Request & { html: string }, res: Response) => {
// html属性它是我们自定义提供 是开发者自己创建的
req.html = 'abc'
res.send('aaa')
})
3.15 类型断言
**断言:**断定某一个类型或值。有些情况下,变量的类型对于我们来说是很明确,但是TS编译器却并不清楚,此时,可以通过类型断言来告诉编译器变量的类型。
语法:[值 as 类型] 或 [<类>值]
类型断言:
假设现在有一个 arg 变量,它既可能是 number 类型也可能是 string 类型,当我们访问它的 length 属性时,就会报错,原因是,arg 如果为 number 类型的话,就没有 length 属性。想要解决这一问题,我们有两种方法:
-
类型保护
function fn(arg: number | string) { // string类型上面是有length属性,但是number它没有,所以你直接用,它就会报错,number也是有可能的 // 类型保护 if (typeof arg === 'string') return arg.length return arg }
-
断言
function fn(arg: number | string) { // string类型上面是有length属性,但是number它没有,所以你直接用,它就会报错,number也是有可能的 // 断言它现在一定是一个string类型的变量 // return (<string>arg).length return (arg as string).length }
值断言:
值断言指用符号!:
表示当前变量一定会有值,不会为空。
class Person {
// 属性暂时可能没有赋值,但是后续我一定会赋值给它,所以可以用一个断言 非空
name!: string
// 也可以在构造函数中赋值
// constructor(name: string) {
// this.name = name
// }
}
链判断符:
判断?.
左侧对象是否为 null 或 undefined ,如果是就返回 undefined。
res?.data相当于:res == null ? undefined : res.data