文章目录
数据类型
1.基础类型
- 安装nodejs环境
- 全局安装TypeScript
npm i typescript -g
3.写ts代码
let str: string = "sz"
console.log(str);
4.控制台终端输入命令
1. tsc -init
2. tsc -w
//就会生成一个js文件。
5.上面那个终端不要关,在开一个,用node来执行js文件
node index.js
//输出 sz
案例1:
支持es6语法
let num: number = 123
let str: string = `${num}`
console.log(str,typeof(str));//123 string
还有一些其他的

布尔值
let b1:boolean = true
let b2:boolean = false
null和undefined
let n:null = null
let b:undefined = undefined
let v1:void = null//严格模式下就报错
let v2:void = undefined
需要在tsconfig.json里面找到strict
设置为false
void一般用与函数的声明。
function myFn():void{
return //返回一个空
}
2.任意类型
上面调试编译太麻烦了,可以全局安装第三方库
npm i ts-node -g
然后打开项目控制台终端
npm init -y
//生成一个package.json文件
然后需要装一个声明文件
npm i @types/node -D
let a: number = 123
console.log(a);
只需要直接在终端输入下面命令,就可以运行ts代码
ts-node index.ts
any
类型:任意类型 ,unknow 不知道的类型.
数据类型可分为下面几大类
1.top type 顶级类型 any ,unknown
2. Object
3.Number,String,Boolean
4.number,string,boolean
5. 1 , 'sz' , false
6. never
any是可以赋值给别等数据类型的,unknown是不可以赋值给别等类型。
let a:any = 1
let b:number = 5
//any是可以
a=b
b=a
//unknown是不可以的
let c:unknown=4
b = c//这样是不行的。
unknown类型和any类型的却别
1.unknown只能赋值给自身或者是any。
2.unknown没有办法读取任何属性 方法也不可以调用
3.any类型比unknown更加安全
案例1:
let sz:unknown = {age:18,open:()=>123}
//这样是读不到的,未知
sz.age
sz.open()
上面any类型是可以的。
3. Object object 以及{}
1.Object
原型链顶端就是这个Object或者function。
意味着所有的原始类型,以及对象类型,最终都指向这个Object。
在ts中,Object就包含了所有类型,拿它就可以等于任何一个值
案例:
let a:Object = 123
let a1:Object = '123'
let a2:Object = []
let a3:Object = {}
let a4:Object = ()=>123
2.object
这个东西一般常用于泛型约束,代表非原始类型的一个类型。
所有原始类型就不支持。支持所有的引用类型
//不支持
let a:object = 123
let a1:object = '123'
let a0:object = false
//支持
let a2:object = []
let a3:object = {}
let a4:object = ()=>123
3. {} 空对象模式
这个和Object一样,也是支持所有数据类型的
let a:{}//new Object
//支持
let a:{} = 123
let a1:{} = '123'
let a2:{} = []
let a3:{} = {}
let a4:{} = ()=>123
补充:
{}(字面量模式),他虽然可以赋值任意数据类型,但是赋值完之后,对象类型,是没法增加和修改的
let a:{}={name:1}
a.age=18//报错
但是赋值基础数据类型貌似可以修改
let a:{}=123
a=18
console.log(a);//18
这个类型还是少用为好。
接口和对象类型
1.接口interface
interface Axxxs{ //注意首字母要大写
name:string
}
let a:Axxxs = { //这样可以定义对象的一个类型
name:'sz'
}
注意:不能多属性,也不能少属性。他俩必须要长的一摸一样
比如想加入一个age属性,必须两个都有
interface Axxxs{ //注意首字母要大写
name:string
age:number
}
let a:Axxxs = { //这样可以定义对象的一个类型
name:'sz',
age:18
}
1.1interface 重名 重合
案例
interface Axxxs{ //注意首字母要大写
name:string
age:number
}
interface Axxxs{
ikun:string
}
let a:Axxxs = { //这样可以定义对象的一个类型
name:'sz',
age:18,
ikun:'cxk'
}
上面遇到重名,他自己就重合,变成这样
interface Axxxs{ //注意首字母要大写
name:string
age:number
ikun:string
}
1.2 任意key
比如这个对象,调接口,后端获得的,里面有杂七杂八各种属性。
但是我只想要name属性,而其他的属性有时候会用到,有时候也不会用到。
但是太多了,又懒的定义。这时候就可以用
索引签名。
interface Axxxs{ //注意首字母要大写
name:string
age:number
[propName:string]:any//这时候下面对象就可以随便定义类型
}
let a:Axxxs = { //这样可以定义对象的一个类型
name:'sz',
age:18,
//下面几个ts就不会进行校验了
a:1,
b:'123'
}
1.3 ?和readonly
?
interface Axxxs{ //注意首字母要大写
name:string
age?:number//这个值可以写,也可以不写
}
let a:Axxxs = { //这样可以定义对象的一个类型
name:'sz',
age:18,
}
readonly
当不想被改变值的时候,就加入这个修饰符
interface Axxxs{
name:string
age:number
readonly id:number
readonly cb:()=>boolean
}
let a:Axxxs = {
name:'sz',
age:18,
id:1
cb:()=>{
return false
}
}
//可以进行调用
a.cb()
//不可以更改
a.cb=()=>{return true}
使用场景:
1.函数,不想随便更改
2.后台返回的id也不想更改
1.4 接口继承
用extend去继承,可以写多个
interface Axxxs extends B{ //相当于两个合并到一块了
name:string
age:number
readonly id:number
readonly cb:()=>boolean
}
interface B{
xxx:string
}
let a:Axxxs = {
name:'sz',
age:18,
id:1,
xxx:"xxx",
cb:()=>{
return false
}
}
1.5 定义函数类型
下面约束了函数的返回值和参数类型
interface Fn {
(name:string):number[]
}
const fn:Fn =function (name:string){
return [1]
}
2.数组类型
2.1数组定义
第一种方式:工作用的会多一点
//数字类型的数组
let arr:number[]=[1,2,3,4]
//boolean类型的数组
let arr:boolean[]=[true,false]
第二种方式:泛型的方式
//数组的普通类型
let arr:Array<boolean> = [true,false]
2.2数组对象
如何定一个对象数组,使用interface
interface X{
name:string
age?:number
}
let arr:X[] = [{name:'sz'},{name:'123'}]
2.3 二维数组
一般方式
let arr:number[][]=[[1],[2],[3]]
泛型的方式
let arr:Array<Array<number>>=[[1],[2],[3]]
如果数组里面各种类型都有,就直接定义成any类型的
let arr:any[] = [1,'asss',true,{}]
案例
function a(...args:any[]){ //或者定义成number类型
console.log(args)//[1,2,3]
}
a(1,2,3)
arguments,类数组(伪数组),它没有数组的一些方法。比如forEach
function a(...args:string[]){
console.log(arguments)//{ '0': '1', '1': '2' }
}
a('1','2')
如何定义?
ts提供一个内置对象 IArguments
,来定义类数组
function a(...args:string[]){
let a:IArguments = arguments
console.log(a)//{ '0': '1', '1': '2' }
}
}
a('1','2')
原理:
function a(...args:string[]){
let a:A = arguments.callee
}
}
a('1','2')
interface A {
callee:Function
length:number
[index:number]:any
}
3.函数类型
3.1 函数定义类型和返回值
普通函数
function add(a: number, b: number): number{ //最后这个number是返回值的类型
return a + b
}
console.log(add(1,2));
箭头函数,类似的
const add = (a:number,b:number):number=>{
return a+b
}
console.log(add(1,2))
3.2 函数的参数
不想穿参数,给新参一个默认值
const add = (a:number=10,b:number = 10):number=>{
return a+b
}
console.log(add())//20
3.3 函数的参数是一个对象
interface User {
name:string
age:number
}
function add(user:User):User{
return user
}
console.log(add({name:'sz',age:18}))//{ name: 'sz', age: 18 }
3.4 函数this类型
ts可以定义this的类型 在js中无法使用。
必须是第一个参数定义this 的类型
interface Obj{
user:number[]
add:(this:Obj,num:number)=>void
}
let obj:Obj = {
user:[1,2,3],
add(this: Obj,num:number) {//传参数的时候忽略第一个this参数,num当成第一个
this.user.push(num)
}
}
obj.add(4)
console.log(obj);
//{ user: [ 1, 2, 3, 4 ], add: [Function: add] }
3.5 函数重载
比如 很多方法只在一个函数里面去实现
同步不同的参数类型,可以在一个函数里面实现增删改查。
//findOne 查找一个参数
//find 查找索引
//add 帮我去做一个新增
//在一个函数里面去实现
let user:number[] = [1,2,3]
//函数重载写法
function findNum(add:number[]):number[]//如果传入的是一个number类型的数组那就做添加
function findNum(id:number):number[]//如果传入id就是单个查询
function findNum():number[]//如果没有传入东西就是查询全部
function findNum(ids?: number | number[]):number[]{ //区分不同的参数实现不同的功能
if (typeof ids == 'number') {
return user.filter(v=>v==ids)
}
else if (Array.isArray(ids)) {
user.push(...ids)
return user
} else { //如果什么都没传,就直接返回一个查询全部
return user
}
}
//什么都没有传,返回user本身全部
console.log(findNum());//[1,2,3]
//传入一个数字
console.log(findNum(2));//[2]
//传入一个数组
console.log(findNum([1,4,5]));//[1, 2, 3, 1, 4, 5 ]
联合类型|类型断言|交叉类型
1.联合类型
//同时支持多个类型
let phone:number | string ='010-90923092s'
声明函数的时候也会使用
比如开发过程中开关状态会显示数字类的0和1,需要将它转为boolean类型
let fn = function (type:number|boolean):boolean{ //如果后台已经转为boolean值了,就参数再加个布尔类型
return !!type //使用强转
}
//!!0 是false
//!!1是true
let result = fn(1)
console.log(result)
2.交叉类型
interface Poeple{
name:string
age:number
}
interface Man{
sex:male
}
const sz=(man:People & Man):void=>{
console.log(man)//{ name: 'sz', age: 18, sex: 'male' }
}
sz({
name:'sz',
age: 18,
sex:'male'
})
3.类型断言
let fn = function (num: number | string): void{
console.log((num as string).length); //6
}
fn('123123')
不能滥用这个类型断言,会导致类型错误
interface A{
run:string
}
interface B{
build:string
}
let fun = (type: A | B) => {
console.log((type as A).run); //可以使用类型断言
// console.log((<A>type).run); //或者这样
}
fun({
build:'123'//像这样,run肯定打印不出来(undefined),所以类型断言不能滥用
})
案例:
(window as any).abc=123//any作为临时断言
案例:
const fn=(type:any):boolean=>{
return type as boolean
}
console.log(fn(1))//数字1,并不会变成布尔值
内置对象 & 代码雨
1.ECMAScript内置对象
案例
let num:Number = new Number(1)
let data:Date = new Date()
let reg:RegExp = new RegExp(/\w/)
let error:Error = new Error('错了')
let xhr:XMLHttpRequest = new XMLHttpRequest()
dom
//HTML(元素名称)Element HTMLElement Elements
let div = document.querySelector('footer')
//当元素是一个集合,可以定义NodeList
let div:NodeList =document.querySelectorAll('footer')
//div forEeach 去遍历当前这个div
//中间获取是动态的
//如果你获取的dom元素不固定的话,就传入HTMLElement就可以实现了
let div:NodeListOf<HTMLDivElement | HTMLElement>=document.querySelectorAll('div footer')
localStorage
let local:Storage = localStorage
let lo:Location = location
//返回的一个类型就要传入相应的类型
let promise:Promise<number> = new Promise((r)=>r(1))
promise.then(res=>{ //res是number
})
let cookie:string = document.cookies
代码雨案例:
let canvas: HTMLCanvasElement = document.querySelector('canvas')!;
if (canvas) {
let ctx = canvas.getContext('2d')
canvas.width = screen.availWidth
canvas.height = screen.availHeight
let str: string[] = 'aaww1231sssss'.split('')
const Arr: number[] = Array(Math.ceil(canvas.width / 10)).fill(0);
console.log(Arr);
const rain = () => {
if (ctx) {
ctx.fillStyle = 'rgba(0,0,0,0.05)'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = '#0f0'
Arr.forEach((item,index) => {
ctx?.fillText(str[Math.floor(Math.random() * str.length)], index * 10, item + 10)
Arr[index] = item > canvas.height||item > 10000 * Math.random() ? 0 :item + 10
})
}
}
setInterval(rain,40)
}
效果:

Class类
1.Class的基本用法,继承和类型约束
案例:
interface Options{
el:string | HTMLElement
}
interface VueCls{
options: Options
init():void
}
//继承
interface Vnode {
tag : string //标签 div section header
text?:string //123
children?:Vnode[]
}
//虚拟dom简单版
class Dom {
//创建节点的方法
createElement(el:string) {
return document.createElement(el)
}
//填充文本方法
setText(el: HTMLElement, text: string | null) {
el.textContent = text
}
//渲染函数,把js描述的dom变成一个真实的dom
render(data:Vnode) {
let root = this.createElement(data.tag)
//如果子节点有值,并且是个数组
if (data.children && Array.isArray(data.children)) {
data.children.forEach(item => {
//不确定里面有多少层,进行递归操作,一直去创建children节点
let child = this.render(item)
root.appendChild(child)
})
} else {
//将文本塞到节点里面
if (typeof data.text !== 'undefined') {
this.setText(root, data.text)
}
}
return root
}
}
class Vue extends Dom implements VueCls {
options: Options //定义一个options
constructor(options: Options) {
super()
this.options = options
this.init()
}
init(): void {
//虚拟dom,就是通过js 去渲染真实的dom
let data:Vnode={
tag: 'div',
children: [
{
tag: "section",
text:"我是子节点1"
},
{
tag: "section",
text:"我是子节点2"
}
]
}
//判断类型,如果是string类型,就进行一个获取,如果不是string,就一个dom节点,直接返回就可以
let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el
//把生成的真实的dom节点给它塞进去
app?.appendChild(this.render(data))
//子类里面去调用父类里面的render方法
this.render(data)
}
}
new Vue({
el:"#app"
})
index.html
<div id="app"></div>
<script src="./index.js"></script>
效果图:
2.class 的修饰符
readonly:只读
private:只能在内部使用
比如
class Dom {
//创建节点的方法
private createElement(el:string) {
return document.createElement(el)
}
//填充文本方法
private setText(el: HTMLElement, text: string | null) {
el.textContent = text
}
}
只能在父类的内部去使用这个方法.
他是一个私有的,外面也没有办法调用
//比如实例化之后
let dom = new DOM()
dom. //没办法调用内部私有的方法
protected:给子类和内部使用的
public:所有的方法默认是public,可以给子类用,可以给自身用,也可以给外部使用
3.super原理
//为什么继承之后这里要加个super
constructor(options: Options) {
super() //父类的prototype.constructor.call
this.options = options
this.init()
}
//super指向的是父类,可以通过super调用父类的方法
super.render()
4.静态方法
属性和方法都是可以使用的
static version(){
return ' 1.0.0'
}
new Vue({
el:"#app"
})
//通过大的实例去调用的
Vue.version()
弹幕:static属性加载在其他属性之前,static初始化的时候别的属性还不存在,所以调用不了。
5.get 和 set
class Ref {
_value : any
constructor(value:any){
this._value = value;
}
//可以自定义去拦截一个读取操作和设置操作
get value(){
return this._value + 'vvvv'
}
set value(newVal){
this._value = newVal + 'sz'
}
}
const ref = new Ref("哈哈哈")
console.log(ref.value)//哈哈哈vvvv
//设置操作
ref.value="坏人"
console.log(ref.value)//坏人szvvvv
所以可以通过git和set作为一个拦截器
抽象类
//abstract 所定义的抽象类
//abstract 所定义的方法 都只能描述,不能进行一个实现
//抽象类无法被实例化
abstract class Vue{
name:string
constructor(name?:string) {
this.name=name!//加!表示明确不为空
}
getName():string {
return this.name
}
//加了abstract就只能进行描述
abstract init(name:string):void
}
//抽象类是不能被实例化的
//new Vue()//会报错
//定义一个派生类去继承这个抽象类
class React extends Vue{
constructor() {
super()
}
//将抽象类里面的方法进行一个实现
init(name:string) {
}
//因为name已经在抽象类里面定义过了,所以在派生类不需要重新定义
setName(name: string) {
this.name=name
}
}
//派生类是可以被实例化的
const react = new React()
react.setName('sz')
console.log(react.getName());//sz
元祖类型
//元祖类型的
let arr:[number,boolean]=[1,true]
arr.push(null)//因为推断它是个number和string的联合类型,除了这两种类型其他类型都是不可以的
如果不想让他修改数组里面的值,并且不想让他使用push,就可以加一个readonly
let arr:readonly [x:number,y?:boolean]=[1]
实际应用场景
let excel:[string,string,number][]= [
['sz','男',18],
['sz','男',18],
['sz','男',18],
['sz','男',18],
]
元祖的类型
let arr:readonly [x:number,y?:boolean]=[1]
type first = type of arr[0]
let arr:readonly [x:number,y:boolean]=[1,false]
type first = typeof arr['length']//2
枚举类型
1.数字枚举
enum Color{
red,
green,
blue
}
console.log(Color.red);//0
console.log(Color.green);//1
console.log(Color.blue);//2
2.增长枚举
enum Color{
red = 1,
green,
blue
}
console.log(Color.red);//1
console.log(Color.green);//2
console.log(Color.blue);//3
3.字符串枚举
enum Color{
red='red',
green='green',
blue='blue'
}
console.log(Color.red);//red
console.log(Color.green);//green
console.log(Color.blue);//blue
弹幕:枚举,是为了可以更清晰明了的看出来具体的某个值的实际意义。
枚举比object的好处也就字面量到值 ,和值 到字面量转换方便点。
3.异构枚举
枚举可以混合字符串和数字成员
enum Color {
yes = 1,
no = 'no'
}
console.log(Color.yes)//1
console.log(Color.no)//no
4.接口枚举
定义一个枚举 定义一个接口A 他有一个属性red 值为Color.yes
声明对象的时候要遵循这个规则
enum Color {
yes = 1,
no = 'no'
}
interface A {
red:Color.yes
}
let obj: A = {
red:Color.yes
}
5.const枚举
let 和 var 都是不允许的声明只能使用const
大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举。 常量枚举通过在枚举上使用 const修饰符来定义
const 声明的枚举会被编译成常量
//如果用const定义
const enum Types{
sucess,
fail
}
let code: number = 0
if (code === Types.sucess) {
}
执行tsc index.ts查看js文件,此昂成一个常量
var code = 0;
if (code === 0 /* Types.sucess */) {
}
如果不加const
,就变成一个对象
var Types;
(function (Types) {
Types[Types["sucess"] = 0] = "sucess";
Types[Types["fail"] = 1] = "fail";
})(Types || (Types = {}));
var code = 0;
if (code === Types.sucess) {
}
6.反向映射
它包含了正向映射( name
-> value
)和反向映射( value
-> name
)
要注意的是 不会为字符串枚举成员生成反向映射。
enum Types{
sucess,
fail
}
let sucess: number = Types.sucess
let key = Types[sucess]
console.log(`value----${sucess}`,`key----${key}`);//value----0 key----sucess
字符串不支持
enum Types{
sucess='456',
}
let sucess: string = Types.sucess
let key = Types[sucess]
console.log(`value----${sucess}`,`key----${key}`);//报错
类型推论|类型别名
类型推论
ts天然支持的类型推论
let str = 'sz'//鼠标放在str上时,他会推断是个string类型
str = 456 //这样是无效的
//数组,对象都是可以类型推论的
let arr = [1,2,4]//推断出是一个number[]
如果是这样,没有给变量进行赋值,也没有给变量定义类型,默认推断是any类型
let str
str=123
str=null //不报错
类型别名
let s =string | number //支持联合类型
let str:s = 'sz'
还可以定义参数
let s = (name:string)=>void
type跟interface的区别
1.interface可以用extends继承,type没法使用
interface A extends B{
}
interface B{
}
//type只能使用交叉类型
type s = number[] & B
2.interface是没有办法写联合类型的,type可以
type str = string | number[]
interface A extends B{
//只能在内部属性定义联合类型
name:string|number
}
3.interface遇到重名的,会进行合并,type是不会的
interface A {
name:string|number
}
interface A{
age:number
}
type的高级用法
左边的值会作为右边值的子类型遵循图中上下的包含关系
//左边的1会作为number的子类型,number包含这个1
type a = 1 extends number ? 1 : 0 //1
type a = 1 extends Number ? 1 : 0 //1
type a = 1 extends Object ? 1 : 0 //1
type a = 1 extends any ? 1 : 0 //1
type a = 1 extends unknow ? 1 : 0 //1
//never是最底层的,包含不了基础类型,所以返回0
type a = 1 extends never ? 1 : 0 //0

never类型
TypeScript 将使用 never 类型来表示不应该存在的状态(很抽象是不是)
// 返回never的函数必须存在无法达到的终点
// 因为必定抛出异常,所以 error 将不会有返回值
function error(message: string): never {
throw new Error(message);
}
// 因为存在死循环,所以 loop 将不会有返回值
function loop(): never {
while (true) {
}
}
never和void的差别
差别一:
//void类型只是没有返回值 但本身不会出错
function Void():void {
console.log();
}
//只会抛出异常没有返回值
function Never():never {
throw new Error('aaa')
}
差异二:
当我们鼠标移上去的时候会发现 只有void和number never在联合类型中会被直接移除
type A = void | number | never
never类的一个应用场景
type A = '小满' | '大满' | '超大满'
function isXiaoMan(value:A) {
switch (value) {
case "小满":
break
case "大满":
break
case "超大满":
break
default:
//是用于场景兜底逻辑
const error:never = value;
return error
}
}
比如新来了一个同事他新增了一个篮球,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG 。
而且这将是一个“隐蔽型”的BUG,如果回归面不够广,很难发现此类BUG。
那 TS 有没有办法帮助我们在类型检查阶段发现这个问题呢?
type A = '小满' | '大满' | '超大满' | "小小满"
function isXiaoMan(value:A) {
switch (value) {
case "小满":
break
case "大满":
break
case "超大满":
break
default:
//是用于场景兜底逻辑
const error:never = value;
return error
}
}
symbol类型
自ECMAScript 2015起,symbol
成为了一种新的原生类型,就像number
和string
一样。
symbol
类型的值是通过Symbol
构造函数创建的。
可以传递参做为唯一标识 只支持 string 和 number类型的参数
let sym1 = Symbol();
let sym2 = Symbol("key"); // 可选的字符串key
1.symbol的值是唯一的
const a1:symbol = Symbol(1)//唯一的
const a2:symbol = Symbol(1)
//他们两个内存地址是不一样的,各自创建各自的内存地址
console.log(a1===a2)//false
扩展:如何让两个symbol返回一个true
console.log(Symbol.for('ss')===Symbol.for('ss'));//true
//for他会在全局的symbol里面找有没有注册过这个key,如果有的话直接拿来用,不会创建新的
//没有的话,他就去创建一个
具体场景
const a1:symbol = Symbol(1)//唯一的
const a2: symbol = Symbol(1)
let obj = {
name: 1,
[a1]: 111,//索引签名的方式
[a2]:222//他们两key一样,但是不会进行覆盖
}
console.log(obj);//{ name: 1, [Symbol(1)]: 111, [Symbol(1)]: 222 }
这就是symbol出现的原因:就是为了解决这个属性的key重复的问题,可以做到一个防重的效果
2.如何获取symbol 的key
如何取这个key
//for in 不能读到symbol
for(let key in obj){
console.log(key)//name
}
//Object.keys
//发现也是读不到symbol
console.log(Object.keys(obj))//['name']
//Object.getOwnPropertyNames也是去不到keys
//可以通过Object.getOwnPropertySymbols()
console.log(Object.getOwnPropertySymbols(obj))//[ Symbol(1), Symbol(1)]
//name和symbol同时取出来
//通过es6的Reflect.ownKeys()
console.log(Reflect.ownKeys(obj))//[ 'name', Symbol(1), Symbol(1) ]
迭代器|生成器
1.生成器
function* gen(){
yield Promise.resolve('sz')//同步异步
yield 'sz111'
}
const man = gen()
//按照调用的顺序来的
console.log(man.next())//{ value: Promise { 'sz' }, done: false }
console.log(man.next())//{ value: 'sz111', done: false }
console.log(man.next())//{ value: undefined, done: true }
//done为true代表已经没有东西可以迭代了
2.迭代器
1.set map
//1.set结构
let set:Set<number> = new Set([1,1,2,3,3,4])//天然去重,元素支持数字和字符串
console.log(set)//Set(4) { 1, 2, 3, 4 }
//2.map结构
let map:Map<any,any> = new Map()
let Arr = [1,2,4]
map.set(Arr,'sz')//可以把数组里面元素当场key
//取值
console.log(map.get(Arr))//sz
function args(){
console.log(arguments)//获取参数的一个集合,它是一个伪数组(类数组)
}
//这也是伪数组,不能使用数组的一些内置方法
let list = document.querySelectorAll('div')
2.Symbol.iterator 迭代器
var arr = [1,2,3,4];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next()); //{ value: 1, done: false }
console.log(iterator.next()); //{ value: 2, done: false }
console.log(iterator.next()); //{ value: 3, done: false }
console.log(iterator.next()); //{ value: 4, done: false }
console.log(iterator.next()); //{ value: undefined, done: true }
测试用例
这个方法可以支持各种类型的数据
let set:Set<number> = new Set([1,1,2,3,3,4])
let map:Map<any,any> = new Map()
let Arr = [1,2,4]
map.set(Arr,'sz')//可以把数组里面元素当场key
const each = (value: any) => {
let It: any = value[Symbol.iterator]()
let next: any = { done: false }
//如果为true就停掉循环
while (!next.done) {
next = It.next()
if (!next.done) {
console.log(next.value);
}
}
}
each(map)//[ [ 1, 2, 4 ], 'sz' ] 分别是key和value
each(set)//1 2 3 4
each(Arr)//1 2 4
3.迭代器语法糖
上面方法语法糖:for of
let set:Set<number> = new Set([1,1,2,3,3,4])
for(let value of set){
console.log(value)
}
for of 对象不能用
for(let value of {name:1}){}//报错,因为对象身上是没有[Symbol.iterator]()方法
4.解构
let [a,b,c]=[4,5,6]
console.log(a,b,c)//4 5 6
底层原理,也是去调用iterator
5.如何让obj支持for of方法
手动添加[Symbol.iterator]()
方法。
let obj={
max:5,
current:0,
[Symbol.iterator](){
return {
max:this.max,
current:this.current,
next(){
if(this.current == this.max){ //迭代结束
return{
value:undefined,
done:true
}
}else{
return{
value:this.current++,
done:false
}
}
}
}
}
}
for (let value of obj) {
console.log(value);//0 1 2 3 4
}
//数组解构
let x = [...obj]
console.log(x)//[0,1,2,3,4]
//对象解构
let a = {...obj}
console.log(a)
//{
// max: 5,
//current: 0,
// [Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
//}
注意:数组解构和对象解构是不一样的
数组解构底层调用的是[Symbol.iterator]()
。
而对象底层不是调用这个。
泛型
1.泛型定义
也可以称之为动态类型
function sz(a:number,b:number):Array<number>{
return [a,b]
}
//如果传入string类型,还需要再写一个
function sz(a:srting,b:string):Array<string>{
return [a,b]
}
所以出现动态类型(泛型)来解决这一问题
function sz<T>(a:T,b:T):Array<T>{
return [a,b]
}
sz(1,2)
//全称:sz<number>(1,2),一般不这样写,让它自己推断就行了
sz('1','2')
sz(false,true)
总结:在定义一个函数的时候,传入的类型不明确,就可以使用泛型,在调用的时候,再去明确它的类型
除了函数之外,interface和type都可以用泛型。
类型别名
type A<T>=string | number | T
let a:A<boolean> = ture //1 'sz'
let a:A<undefined> = undefined
let a:A<null> = null
接口interface
interface Data<T>{
msg:T
}
let data:Data<string>={
msg:'sz无了'
}
其他泛型用法
//泛型支持多个
function add<T,K>(a:T,b:K):Array<T | K>{
return [a,b]
}
console.log(add(1,false));//[ 1, false ]
add()//什么都不传,推断的就是个number
2.axios封装
data.json
{
"message":"成功",
"code":200
}
index.ts
const axios = {
get<T>(url:string):Promise<T>{
return new Promise((resolve,reject)=>{
let xhr: XMLHttpRequest = new XMLHttpRequest()
//发起请求的一个方式
xhr.open('GET', url)
//监听状态变化的方法
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status==200) {
resolve(JSON.parse(xhr.responseText))
}
}
xhr.send(null)
})
}
}
interface Data {
message: string,
code:number
}
axios.get<Data>('./data.json').then(res => {
console.log(res);//
})
1.控制台输入命令tsc
2.vscode安装live serve插件
3.在index.html文件右键选择Open with Live Serve
打印结果:
泛型约束
1.泛型约束案例
在类型后面跟一个extends,在跟一个约束的类型
function add<T extends number>(a:T,b:T){
return a + b
}
console.log(add(1,2));//3
案例2:
interface Len {
length:number
}
function fn<T extends Len>(a: T) {
console.log(a.length);
}
fn('13234')//5
fn([1,2,3,4])//4
2. 约束对象的key
let obj = {
name: 'sz',
sex:'男'
}
///age keyof
type Key = keyof typeof obj//将对象身上的key推断成一个联合类型
//第一个参数T希望传入一个引用类型,第二个参数K希望传入对象身上的一个key
function ob<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
console.log(ob(obj,'name'));//sz
如果要约束对象的key,一定要使用keyof
keyof高级用法:
interface Data{
name: string,
age: number,
sex:string
}
//使用泛型工具,将他们都变成可选状态
type Options<T extends object> = {
//类似于for in
[Key in keyof T]?:T[Key]
}
type B =Options<Data>
发现都加上了问号

同理不想加?
,可以给它加个readonly
readonly [Key in keyof T]:T[Key]
tsconfig.json配置文件
1.生成sconfig.json
tsc --init
2.配置详情
"compilerOptions": {
"incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
"tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
"diagnostics": true, // 打印诊断信息
"target": "ES5", // 目标语言的版本
"module": "CommonJS", // 生成代码的模板标准
"outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
"lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
"allowJS": true, // 允许编译器编译JS,JSX文件
"checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
"outDir": "./dist", // 指定输出目录
"rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
"declaration": true, // 生成声明文件,开启后会自动生成声明文件
"declarationDir": "./file", // 指定生成声明文件存放目录
"emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
"sourceMap": true, // 生成目标文件的sourceMap文件
"inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
"declarationMap": true, // 为声明文件生成sourceMap
"typeRoots": [], // 声明文件目录,默认时node_modules/@types
"types": [], // 加载的声明文件包
"removeComments":true, // 删除注释
"noEmit": true, // 不输出文件,即编译后不会生成任何js文件
"noEmitOnError": true, // 发送错误时不输出任何文件
"noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
"importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
"downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
"strict": true, // 开启所有严格的类型检查
"alwaysStrict": true, // 在代码中注入'use strict'
"noImplicitAny": true, // 不允许隐式的any类型
"strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
"strictFunctionTypes": true, // 不允许函数参数双向协变
"strictPropertyInitialization": true, // 类的实例属性必须初始化
"strictBindCallApply": true, // 严格的bind/call/apply检查
"noImplicitThis": true, // 不允许this有隐式的any类型
"noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
"noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
"noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
"noImplicitReturns": true, //每个分支都会有返回值
"esModuleInterop": true, // 允许export=导出,由import from 导入
"allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
"moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { // 路径映射,相对于baseUrl
// 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
"jquery": ["node_modules/jquery/dist/jquery.min.js"]
},
"rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
"listEmittedFiles": true, // 打印输出文件
"listFiles": true// 打印编译的文件(包括引用的声明文件)
}
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
"src/**/*"
],
// 指定一个排除列表(include的反向操作)
"exclude": [
"demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
"files": [
"demo.ts"
]
介绍几个常用的
1.include
指定编译文件默认是编译当前目录下所有的ts文件
2.exclude
指定排除的文件
3.target
指定编译js 的版本例如es5 es6
4.allowJS
是否允许编译js文件
5.removeComments
是否在编译过程中删除文件中的注释
6.rootDir
编译文件的目录
7.outDir
输出的目录
8.sourceMap
代码源文件
9.strict
严格模式
10.module
默认common.js 可选es6模式 amd umd 等
namespace命名空间
我们在工作中无法避免全局变量造成的污染,TypeScript提供了namespace 避免这个问题出现
-
内部模块,主要用于组织代码,避免命名冲突。
-
命名空间内的类默认私有
-
通过
export
暴露 -
通过
namespace
关键字定义TypeScript与ECMAScript 2015一样,任何包含顶级
import
或者export
的文件都被当成一个模块。相反地,如果一个文件不带有顶级的import
或者export
声明,那么它的内容被视为全局可见的(因此对模块也是可见的)
ok,让我们看一个小例子
1.命名空间案例
命名空间中通过export
将想要暴露的部分导出
如果不用export 导出是无法读取其值的
namespace a {
export const Time: number = 1000
export const fn = <T>(arg: T): T => {
return arg
}
fn(Time)
}
namespace b {
export const Time: number = 1000
export const fn = <T>(arg: T): T => {
return arg
}
fn(Time)
}
a.Time
b.Time
2.嵌套命名空间
namespace a {
export namespace b {
export class Vue {
parameters: string
constructor(parameters: string) {
this.parameters = parameters
}
}
}
}
let v = a.b.Vue
new v('1')
3.抽离命名空间
a.ts
export namespace V {
export const a = 1
}
b.ts
import {V} from '../observer/index'
console.log(V); //{a:1}
简化命名空间
namespace A {
export namespace B {
export const C = 1
}
}
import X = A.B.C
console.log(X);//1
4.合并命名空间
重名的命名空间会合并

三斜线指令
弹幕:
三斜线注释主要用于旧版本的TypeScript或与特定工具和库的集成中。
在新的TypeScript项目中,不再需要使用三斜线注释进行模块引用,而是通过import语句来导入其他模块。
ts3.0以后就不建议用这个了,建议用import.
三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。
三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。
/// 指令是三斜线指令中最常见的一种。 它用于声明文件间的 依赖。
三斜线引用告诉编译器在编译过程中要引入的额外的文件。
你也可以把它理解能import,它可以告诉编译器在编译过程中要引入的额外的文件
例如a.ts
namespace A {
export const fn = () => 'a'
}
b.ts
namespace A {
export const fn2 = () => 'b'
}
index.ts
引入之后直接可以使用变量A
///<reference path="./index2.ts" />
///<reference path="./index3.ts" />
console.log(A);
声明文件引入
例如,把 /// 引入到声明文件,表明这个文件使用了 @types/node/index.d.ts里面声明的名字; 并且,这个包需要在编译阶段与声明文件一起被包含进来。
仅当在你需要写一个d.ts文件时才使用这个指令。
///<reference types="node" />
注意事项:
如果你在配置文件 配置了noResolve 或者自身调用自身文件会报错

声明文件
1.声明文件declare
ts当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
/// <reference /> 三斜线指令
例如我们有一个express 和 axios
发现express 报错了
让我们去下载他的声明文件
npm install @types/node -D
那为什么axios 没有报错
我们可以去node_modules 下面去找axios 的package json

发现axios已经指定了声明文件 所以没有报错可以直接用
通过语法declare 暴露我们声明的axios 对象
declare const axios: AxiosStatic;
如果有一些第三方包确实没有声明文件我们可以自己去定义
名称.d.ts 创建一个文件去声明
案例手写声明文件
index.ts
2.手写声明文件
index.ts
import express from 'express'
const app = express()
const router = express.Router()
app.use('/api', router)
router.get('/list', (req, res) => {
res.json({
code: 200
})
})
app.listen(9001,()=>{
console.log(9001)
})
express.d.ts
declare module 'express' {
interface Router {
get(path: string, cb: (req: any, res: any) => void): void
}
interface App {
use(path: string, router: any): void
listen(port: number, cb?: () => void): void
}
interface Express {
(): App
Router(): Router
}
const express: Express
export default express
}
Mixins混入
TypeScript 混入 Mixins 其实vue也有mixins这个东西 你可以把他看作为合并
1.对象混入
可以使用es6的Object.assign 合并多个对象
此时 people 会被推断成一个交差类型 Name & Age & sex;
interface Name{
name:string
}
interface Age{
age:number
}
interface Sex{
sex:boolean
}
let people1: Name = {
name:'sz'
}
let people2: Age = {
age:15
}
let people3: Sex = {
sex:true
}
const people = Object.assign(people1, people2, people3)
console.log(people);//{ name: 'sz', age: 15, sex: true }
2.类的混入
首先声明两个mixins类 (严格模式要关闭不然编译不过)
class A {
type: boolean = false;
changeType() {
this.type = !this.type
}
}
class B {
name: string = '张三';
getName(): string {
return this.name;
}
}
下面创建一个类,结合了这两个mixins
首先应该注意到的是,没使用extends而是使用implements。 把类当成了接口
我们可以这么做来达到目的,为将要mixin进来的属性方法创建出占位属性。 这告诉编译器这些成员在运行时是可用的。 这样就能使用mixin带来的便利,虽说需要提前定义一些占位属性
class C implements A,B{
type:boolean
changeType:()=>void;
name: string;
getName:()=> string
}
最后,创建这个帮助函数,帮我们做混入操作。 它会遍历mixins上的所有属性,并复制到目标上去,把之前的占位属性替换成真正的实现代码
Object.getOwnPropertyNames()可以获取对象自身的属性,除去他继承来的属性,
对它所有的属性遍历,它是一个数组,遍历一下它所有的属性名
Mixins(C, [A, B])
function Mixins(curCls: any, itemCls: any[]) {
itemCls.forEach(item => {
console.log(item);//[class A] [class B]
Object.getOwnPropertyNames(item.prototype).forEach(name => {
curCls.prototype[name] = item.prototype[name]
})
})
}
let ccc= new C()
console.log(ccc.type)//false
ccc.changeType()
console.log(ccc.type)//true
装饰器Decorator
面试经常问,装饰器可以干什么,优势是什么。
Decorator 装饰器是一项实验性特性,在未来的版本中可能会发生改变
它们不仅增加了代码的可读性,清晰地表达了意图,而且提供一种方便的手段,增加或修改类的功能
若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json
里启用编译器选项
1.类装饰器
const Base:ClassDecorator = (target) =>{ //首字母大写 target是Http的构造函数
console.log(target)// [class Http]
target.prototype.name = "sz",
target.prototype.fn = () => {
console.log('我是sz');
}
}
@Base
class Http{
}
const http = new Http() as any
http.fn()
console.log(http.name);
不去破坏它自身的结构,从而给这个类去增加方法和属性
2.装饰器工厂
其实也就是一个高阶函数 外层的函数接受值 里层的函数最终接受类的构造函数
const Base = (name:string) => { //首字母大写 target是Http的构造函数
const fn: ClassDecorator = (target) => {
console.log(target)
target.prototype.name = name,
target.prototype.fn = () => {
console.log('我是sz');
}
}
return fn
}
@Base('sz123')
class Http{
}
const http = new Http() as any
console.log(http.name);//sz123
3.方法装饰器
实现一个get请求
import axios from "axios";
import 'reflect-metadata';
const Get = (url: string) => {
const fn:MethodDecorator= (target,_:any,descriptor:PropertyDescriptor)=>{
// console.log(target, key, descriptor);
// {} getList {
// value: [Function: getList],
// writable: true,
// enumerable: false,
// configurable: true
// }
const key = Reflect.getMetadata('key',target)
axios.get(url).then(res => {
descriptor.value(key ?res.data:res[key].data)
})
}
return fn
}
const Result =()=>{
//参数装饰器
const fn: ParameterDecorator = (target,key,index) => { //index是参数所在的位置
Reflect.defineMetadata('key','result',target)
}
return fn
}
class Http{
@Get('https://api.apiopen.top/api/getHaoKanVideo?page=0&size=10')
getList(@Result() data:any){
console.log(data)//
}
create(){
}
}
4.属性装饰器
const Name: PropertyDecorator = (target,key) => {
console.log(target,key);//{} name
}
class Http{
@Name
name: string
constructor() {
this.name='sz'
}
}
构建ts项目
1.Rollup构建TS项目
Rollup构建TS项目
安装依赖
1.全局安装rollup npm install rollup-g
2.安装TypeScript npm install typescript -D
3.安装TypeScript 转换器 npm install rollup-plugin-typescript2 -D
4安装代码压缩插件 npm install rollup-plugin-terser -D
5 安装rollupweb服务 npm install rollup-plugin-serve -D
6 安装热更新 npm install rollup-plugin-livereload -D
7引入外部依赖 npm install rollup-plugin-node-resolve -D
8安装配置环境变量用来区分本地和生产 npm install cross-env -D
9替换环境变量给浏览器使用 npm install rollup-plugin-replace -D
webpack rollup打包对比


配置json文件
npm init -y
{
"name": "rollupTs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "cross-env NODE_ENV=development rollup -c -w",
"build":"cross-env NODE_ENV=produaction rollup -c"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"cross-env": "^7.0.3",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-serve": "^1.1.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.1",
"typescript": "^4.5.5"
}
}
配置rollup文件
console.log(process.env);
import ts from 'rollup-plugin-typescript2'
import path from 'path'
import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'
import { terser } from 'rollup-plugin-terser'
import resolve from 'rollup-plugin-node-resolve'
import repacle from 'rollup-plugin-replace'
const isDev = () => {
return process.env.NODE_ENV === 'development'
}
export default {
input: "./src/main.ts",
output: {
file: path.resolve(__dirname, './lib/index.js'),
format: "umd",
sourcemap: true
},
plugins: [
ts(),
terser({
compress: {
drop_console: !isDev()
}
}),
repacle({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
resolve(['.js', '.ts']),
isDev() && livereload(),
isDev() && serve({
open: true,
openPage: "/public/index.html"
})
]
}
配置tsconfig.json
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "ES2015", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
npm run dev 启动
2.webpack构建TS项目
1.创建一个项目
npm init -y //生成package.json文件
安装依赖
安装webpack npm install webpack -D
webpack4以上需要 npm install webpack-cli -D
编译TS npm install ts-loader -D
TS环境 npm install typescript -D
热更新服务 npm install webpack-dev-server -D
HTML模板 npm install html-webpack-plugin -D
然后输入
tsc --init //生成tsconfig.json文件
新建文件夹->文件
src -> index.ts
public -> index.html
新建一个webpack.config.js
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: "./src/index.ts",
mode: "development",
output: {
path: path.resolve(__dirname, './dist'),
filename: "index.js"
},
stats: "none",
resolve: {
//匹配后缀的名字
extensions: ['.ts', '.js'],
alias: {
'@': path.resolve(__dirname, './src')
}
},
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader"
}
]
},
devServer: {
port: 1988,
proxy: {}
},
plugins: [
new htmlWebpackPlugin({
template: "./public/index.html"
})
]
}
3.esbuild + swc构建ts项目
- esbuild是go语言编写的并且是多线程执行,性能是js的好几十倍,所以很快。
- 无需缓存即可实现基础打包
- 支持 ES6 跟 CommonJS 模块
- 支持ES 6 Tree Shaking
- 体积小
- 插件化
- 其他
- 内置支持编译 jsx
2.swc是用rust
写的,所实现的功能跟babel一样,es6语法转es5,但是速度比babel
更快,前端基建工具使用rust的是越来越多了,未来可能还会有一个替代postCss
的
npm install @swc/core esbuild @swc/helpers
其中,@swc/core
是 swc 的核心包,用于编译 JavaScript 和 TypeScript 代码;esbuild
是一个快速的 JavaScript 和 TypeScript 构建工具;@swc/helpers
是 swc 的辅助包,用于转换 JSX 代码。
config.ts文件
import esbuild from 'esbuild'//打包工具
import swc from '@swc/core'//类似于babel es6 转 es5
import fs from 'node:fs'
await esbuild.build({
entryPoints: ['./index.ts'], //入口文件
bundle: true, //模块单独打包
loader: {
'.js': 'js',
'.ts': 'ts',
'.jsx': 'jsx',
'.tsx': 'tsx',
},
treeShaking:true,
define: {
'process.env.NODE_ENV': '"production"',
},
plugins: [
{
//实现自定义loader
name: "swc-loader",
setup(build) {
build.onLoad({ filter: /\.(js|ts|tsx|jsx)$/ }, (args) => {
// console.log(args);
const content = fs.readFileSync(args.path, "utf-8")
const { code } = swc.transformSync(content, {
filename: args.path
})
return {
contents: code
}
})
},
}
],
outdir: "dist"
})
测试demo
export const a:number = 1
export const b:string = 'ikun'
let x = 1
let fn = () => 123
console.log(x,fn);
转移之后的
export var a = 1;
export var b = "ikun";
var x = 1;
var fn = function() {
return 123;
};
console.log(x, fn);
实战插件编写
typescript封装LocalStorage并设置过期时间
目录结构

package.json是新建的
然后输入命令,生成里面的内容
npm init -y
然后tsconfig.json不需要新建,输入命令自动生成
tsc --init
然后在tsconfig.json里面将这些改动
"module":"ESNext" ,
"moduleResolution": "node",
"strict":false//关闭严格模式
package.json:
{
"type" :"module",
"name": "storage",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build":"rollup -c"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"rollup": "^3.28.0",
"rollup-plugin-typescript2": "^0.35.0",
"typescript": "^5.1.6"
}
}
rollup.config.js
import ts from 'rollup-plugin-typescript2'
import path from 'path'
import { fileURLToPath } from 'url'
const metaUrl = fileURLToPath(import.meta.url)
const dirName = path.dirname(metaUrl)
export default {
input: './src/index.ts',
output: {
file: path.resolve(dirName, './dist/index.js')
},
plugins: [ts()]
}
src/enum/index.ts
//字典 Dictionaries expire过期时间key permanent永久不过期
export enum Dictionaries {
expire = '_expire_',
permanent = 'permanent'
}
src/type/index.ts
import { Dictionaries } from "../enum"
export type Key = string
export type Expire = Dictionaries.permanent | number //时间戳
export interface Result <T>{
message: string
value:T | null
}
export interface Data <T>{
value: T,
[Dictionaries.expire]:Expire
}
export interface StorageCls{
get:<T>(key:Key) => void,
set:<T>(key:Key,value:T,expire:Expire)=>void
remove:(key: Key)=> void,
clear:()=>void,
}
STORAGE/src/index.ts
//expire过期时间key,permanent永不过期
import { StorageCls, Key,Expire,Data,Result } from "./type";
import { Dictionaries } from "./enum";
export class Storage implements StorageCls {
set<T>(key: Key, value: T, expire:Expire = Dictionaries.permanent) { //不传这个时间,默认就是永久的,如果传了,就支持过期时间
//格式化对象,要按照格式存
const data = {
value,//用户传入的value
[Dictionaries.expire]:expire //过期的时间
}
localStorage.setItem(key,JSON.stringify(data))
}
get<T>(key: Key):Result<T | null> {
const value = localStorage.getItem(key)
if (value) {
const data: Data<T> = JSON.parse(value)
//如果是number,那传入的肯定是时间戳,并且需要获取当前时间判断一下,有没有过期
const now = new Date().getTime()
if (typeof data[Dictionaries.expire] == 'number' && data[Dictionaries.expire] < now) {
this.remove(key)
return {
message: `您的${key}已过期`,
value:null
}
} else {
return {
message: '成功',
value:data.value
}
}
} else {
return {
message: '值无效!',
value:null
}
}
}
remove(key: Key) {
localStorage.removeItem(key)
}
clear() {
localStorage.clear()
}
}
TS编写发布订阅模式
概述
什么是发布订阅模式,其实小伙伴已经用到了发布订阅模式例如addEventListener,Vue evnetBus(事件总线)
都属于发布订阅模式
简单来说就是 你要和 大傻 二傻 三傻打球,大傻带球,二傻带水,三傻带球衣。全都准备完成后开始打球。
思维导图
首先 需要定义三个角色 发布者 订阅者 调度者

具体代码
on订阅/监听
emit 发布/注册
once 只执行一次
off解除绑定
interface EventFace {
on: (name:string,fn:Function) => void,
emit: (name:string,...args:Array<any>) => void,
off: (name: string, fn: Function) => void,
once:(name:string,fn:Function)=>void,
}
interface List {
[key:string]:Array<Function>
}
class Dispatch implements EventFace {
lists:List
constructor() {
this.lists ={}
}
on(name:string,fn:Function) {
const callback = this.lists[name] || []
callback.push(fn)
this.lists[name] = callback
// console.log(this.lists);
}
emit(name:string,...args:Array<any>) {
let eventName = this.lists[name]
if (eventName) {
eventName.forEach(fn => {
fn.apply(this,args)
})
} else {
console.error(`名称错误${name}`);
}
}
off(name: string, fn: Function) {
let eventName = this.lists[name]
if (eventName && fn) {
//通过索引将函数删掉
let index = eventName.findIndex(fns => fns === fn)
eventName.splice(index,1)
} else {
console.error(`名称错误${name}`);
}
}
once(name:string,fn:Function) {
let de = (...args:Array<any>) => {
fn.apply(this, args)
this.off(name,de)
}
this.on(name,de)
}
}
const o = new Dispatch()
o.on('post',(...args:Array<any>)=>{
console.log(args,1);
})
o.once('post', (...args:Array<any>) => {
console.log(args,'once');
})
// const fn = (...args:Array<any>) => {
// console.log(args,2);
// }
// o.on('post', fn)
// o.off('post',fn)
o.emit('post', 1, false, { name: 'sz' })
o.emit('post',2,true,{name:'sz'})
proxy Reflect
1. proxy
学习proxy对象代理
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
是代理接口的,es6新增语法
target
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
handler.get() 本次使用的get
属性读取操作的捕捉器。
handler.set() 本次使用的set
属性设置操作的捕捉器。
案例:
let person = {name:'sz',age:19}
//proxy支持对象 数组 函数 set map
person.name //取值
person.name='ssss'//赋值
let personProxy = new Proxy(person,{
//拦截取值的操作
get(){},
//拦截赋值的操作
//person name xxx(赋值) person
set(target,key,value,receiver){
return true
},
//拦截函数的调用
apply(){},
//拦截in操作符
has(){},
//拦截for in
ownKeys(){},
//拦截new操作符
construct(){},
//拦截删除操作
deleteProperty(target,p){},
})
案例2:拦截器可以干什么
let person = {name:'sz',age:19}
let personProxy = new Proxy(person,{
get(target,key,receiver){
if(target.age<=18){
//Reflect会直接操作对象,并且参数是一模一样
return Reflect.get(target,key,receiver)
}else{
return 'sz成年了'
}
}
})
console.log(personProxy.age)//sz成年了
person.age = 17
console.log(personProxy.age);//17
2.Reflect
//常规对象取值的方式
let person = {name:'sz',age:19}
person.name
//Reflect取值
Reflect.get(person,'name',person)//第三个参数保证上下文的一个正确
//set也是一样的
console.log(Reflect.set(person,'name','sz123',person))
console.log(person)// {name:'sz123',age:19}
3.mobx observable
案例
简单的监听的一个模式
//观察者模式
//创建一个存储器
const list:Set<Function> = new Set()
//创建一个订阅函数
const autorun = (cb:Function)=>{
if(!list.has(cb)){
list.add(cb)
}
} //接受一个回掉函数
//提供一个可观测的数据
const observable = <T extends object>(params:T)=>{ //进行一个泛型的约束,因为proxy只支持object引用类型的参数
return new Proxy(params,{
//拦截set
set(target,key,value,receiver){
//配合Reflect返回一个boolean值
const result = Reflect.set(target,key,value,receiver)
//如果数据有变化的时候,通知订阅者
list.forEach(fn=>fn())
return result
}
})
}
//提供一个可观察的数据
const personProxy = observable({name:'sz',attr:'先生'})
//订阅
autorun(()=>{
console.log('有变化了')
})
personProxy.attr='sz威猛先生'//打印有变化了
协变 逆变 双向协变
所谓的类型兼容性,就是用于确定一个类型是否能赋值给其他的类型。typeScript中的类型兼容性是基于结构类型的(也就是形状),如果A要兼容B 那么A至少具有B相同的属性。
1.协变
也可以叫鸭子类型
什么是鸭子类型?
一只鸟 走路像鸭子 ,游泳也像,做什么都像,那么这只鸟就可以成为鸭子类型。
举例说明
//主类型
interface A {
name:string
age:number
}
//子类型
interface B {
name:string
age:number
sex:string
}
let a:A = {
name:"老墨我想吃鱼了",
age:33,
}
let b:B = {
name:"老墨我不想吃鱼",
age:33,
sex:"女"
}
//只要子类型里面所有的属性可以完全的去覆盖主类型里面所有的属性,那这个赋值是允许的
a = b
A B 两个类型完全不同但是竟然可以赋值并无报错B类型充当A类型的子类型,当子类型里面的属性满足A类型就可以进行赋值,也就是说不能少可以多,这就是协变。
2.逆变
通常发生在一个函数上
//主类型
interface A {
name:string
age:number
}
//子类型
interface B {
name:string
age:number
sex:string
}
let fna = (params:A)=>{}
let fnb = (params:B)=>{}
//fna = fnb//这样是不行的
fnb = fna//这样是可以的 安全的
//最后调用fnb函数的时候,执行的是fna
3.双向协变
tsc --init命令
生成tsconfig.json文件
tsconfig strictFunctionTypes 设置为false 支持双向协变 fna fnb 随便可以来回赋值

fna = fnb
fnb = fna
set,map,weakSet,weakMap
1.set
集合是由一组无序且唯一(即不能重复)的项组成的,可以想象成集合是一个既没有重复元素,也没有顺序概念的数组
属性
size:返回字典所包含的元素个数
操作方法
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为 Set 的成员。
clear():清除所有成员,无返回值。
size: 返回set数据结构的数据长度
let set:Set<number> = new Set([1,2,3,4,6,6,6,6]) //天然去重,引用类型除外
console.log(set)//[1,2,3,4,5,6]
set.add(7)// set 为[1,2,3,4,5,6,7]
set.delete(7)//set为[1,2,3,4,5,6]
set.has(6)//true
set.clear()//[]
2.map
它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
let obj = { name: '小满' }
let map: Map<object, Function> = new Map()
map.set(obj, () => 123)
map.get(obj)
map.has(obj)
map.delete(obj)
map.size
3.WeakSet和WeakMap
Weak 在英语的意思就是弱的意思,weakSet 和 weakMap 的键都是弱引用,不会被计入垃圾回收
首先obj引用了这个对象 + 1,aahph也引用了 + 1,wmap也引用了,但是不会 + 1,应为他是弱引用,不会计入垃圾回收,因此 obj 和 aahph 释放了该引用 weakMap 也会随着消失的,但是有个问题你会发现控制台能输出,值是取不到的,应为V8的GC回收是需要一定时间的,你可以延长到500ms看一看,并且为了避免这个问题不允许读取键值,也不允许遍历,同理weakSet 也一样
let obj:any = {name:'小满zs'} //1
let aahph:any = obj //2
//weakmap的key只能是引用类型
let wmap:WeakMap<object,string> = new WeakMap()
wmap.set(obj,'sadsad') //2 他的键是弱引用不会计数的
obj = null // -1
aahph = null;//-1
//v8 GC 不稳定 最少200ms
setTimeout(()=>{
console.log(wmap)
},500)
Partial & Pick
TS内置高级类型Partial Pick
1.Partial
源码:
/**
* Make all properties in T optional
将T中的所有属性设置为可选
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
使用前
type Person = {
name:string,
age:number
}
type p = Partial<Person>
转换后全部转为了可选
type p ={
name?:string | undefined,
age?:number | undefined;
}
keyof
是干什么的?in
是干什么的??
是将该属性变为可选属性T[P]
是干什么的?
1 keyof
我们讲过很多遍了 将一个接口对象的全部属性取出来变成联合类型
2 in
我们可以理解成for in P 就是key 遍历 keyof T 就是联合类型的每一项
3 ?
这个操作就是将每一个属性变成可选项
4 T[P]
索引访问操作符,与 JavaScript 种访问属性值的操作类似
2.Pick
从类型定义T的属性中,选取指定一组属性,返回一个新的类型定义
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Person = {
name:string,
age:number,
text:string
address:string
}
type Ex = "text" | "age"
//相当于把一些属性筛选出来
type A = Pick<Person,Ex>
/**
type A = {
text:string;
age:number;
}
*/
# Record&Readonly
1.Readonly
跟Partial差不多,它将属性变成只读的
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
2.Record
案例:
type Rec<K extends keyof any, T> = {
[P in K]: T;
};
type key = string | number | symbol
type Person = {
name:string,
age:number,
text:string
}
type K = "A" | "B" | "C"
type B = Rec<K,Person>
let obj:B={
A:{name:"sz",age:22,text:'男人'},
B:{name:"sz",age:22,text:'男人'},
C:{name:"sz",age:22,text:'男人'}
}

也可以支持nuber和symbol类型
type K = 1|2|3
let obj:B={
1:{name:"sz",age:22,text:'男人'},
2:{name:"sz",age:22,text:'男人'},
3:{name:"sz",age:22,text:'男人'}
}
1 keyof any 返回 string number symbol 的联合类型
2 in 我们可以理解成for in P 就是key 遍历 keyof any 就是string number symbol类型的每一项
3 extends来约束我们的类型
4 T 直接返回类型
做到了约束 对象的key 同时约束了 value.
infer用法
1.条件类型推断案例
infer是ts新增的关键字,充当占位符
来实现一个条件类型推断的例子
定义一个类型 如果是数组类型 就返回 数组元素的类型 否则 就传入什么类型 就返回什么类型
type TYPE<T> = T extends Array<any> ? T[number] : T
type A = TYPE<(boolean | string)[]>
type B = TYPE<null>
可以通过infer操作来简化
type Infer<T> = T extends Array<infer U> ? U : T
type A = Infer<(string | Symbol)[]>
例子2:配合tuple 转换 union 联合类型
type TupleToUni<T> = T extends Array<infer E> ? E : never
type TTuple = [string, number];
type ToUnion = TupleToUni<TTuple>; // string | number
2.infer类型提取
1.提取头部元素
type Arr = ['a','b','c']
type First<T extends any[]> = T extends [infer one,...arr[]] ?one:[]
type a = First<Arr>//取到的是上面的a,第一个
2.取最后一个
type Arr = ['a','b','c']
type Last<T extends any[]> = T extends [...arr[],infer Last] ?one:[]
type a = Last<Arr>//取到的是上面的a,第一个
3.删掉最后一个元素
type Arr = ['a','b','c']
type pop<T extends any[]> = T extends [...inter Rest,unknow] ? Rest : []
type a = pop<Arr>
4.删除第一个
type Arr = ['a','b','c']
type shift<T extends any[]> = T extends [unknow,...inter Rest] ? Rest : []
type a = shift<Arr>
3.递归
将数组翻转过来
type Arr = [1,2,3,4]
type ReverArr<T extends any[]> = T extends [infer First,...inter rest] ?[...ReverArr<rest>,First] : T//递归操作
type Arrb = ReverArr<Arr>