TS的回顾记录一

37 阅读9分钟

ts是什么呢?

我个人的感觉就是加强版的js,js是没有类型的,js的错误是在执行时才会发现,而ts在开发编译时就可以发现;能帮助开发人员检测出错误并修改,提前发现错误,减少改Bug时间

ts的概述

  1. js是没有类型的,ts是拥有类型的js,可以编译成js.同时拥有类型检查,语言扩展,工具属性等
  2. 它的好处是:接口定义直接生成文档,让前端开发者重塑类型思维
  3. 啥是强类型语言:不允许改变变量的数据类型,除非进行强制类型转换
  4. 静态类型语言:在编译阶段确定所有变量类型
  5. 动态类型语言:在执行阶段确定所有变量类型
  6. 提前检查类型,方便v8编译器进行优化

ts的学习

1.环境的搭建
  1. 新建ts-study文件 用vscode打开
  2. 在根目录打开终端,执行npm init-y 生成package.json文件
  3. 安装ts yarn add typescript
  4. 安装 ts-node-dev(编译我们的ts文件,并在修改文件后重新启动) yarn add ts-node-dev dev
  5. 新建tsconfig.json文件,用来配置ts的编译规则
  6. 新建demo文件夹,以及demo1.ts文件,代码如下:
const data:string='hello ts11'
console.log(data)
  1. 修改package.json中的代码:
{
  "name": "ts-studying",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "ts-node-dev --respawn --transpile-only demos/demo1.ts"  //编译的命令
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "typescript": "^4.8.4"
  },
  "devDependencies": {
    "ts-node-dev": "^2.0.0"
  }
}

执行yarn dev的命令后:控制台打印如下:

Snipaste_2022-11-08_23-27-57.png

2.基础类型的学习:

js的数据类型有:

  • Boolean
  • Number
  • String
  • Array
  • Function
  • Object
  • Symbol
  • undefined
  • null

ts的数据类型有:

  • number:数字
  • string:字符串
  • boolean :布尔类型
  • array :数组
  • tuple :元组
  • void :一般用于函数没有返回值的类型
  • any:任意类型,一般不建议使用,失去了类型检查的效果
  • nerver:函数永远不会有返回值时,函数永远会抛出一个错误时,这两种情况使用
  • 元组:就是特殊的数组
  • 枚举:主要是用于一些固定参数的结合
  • 高级类型:接口interface type,联合类型等
1.boolean,number,string

不要为了类型而写类型,如果定义的变量是固定的值,类型是可以不用写的,ts是可以反向推断出来的

// boolean,number,string
let isDone:boolean=false
let num=2    //可以不用写number,因为是固定值,ts是可以直接反向推断出来的
let str:string='taotao'

2.数组与元组

数组就是里面元素的类型是一样的,元组就是里面元素的类型是不一样的

在文件夹demo中新建demo2.ts文件,代码如下:

// 已有数组,下面是数字的数组,字符串的数组是类似的
let arr:number[]=[1,2,3]  //第一种形式定义数组
let arr2:Array<number>=[1,2,3]  //第二种形式定义数组
let arr3=new Array<number>(4)   //定义一个空的长度为4的数组
console.log(arr3)  //[ <4 empty items> ]

// 使用接口来定义一个数组
interface NumberArray{
    [index:number]:number
}
let arr4:NumberArray=[1,2,3]

// 类数组
function sum(){
    let args:IArguments=arguments   //类数组 arguments  是有内置类型的IArguments
    // args.length
    // interface IArguments {  //lib.es5.d.ts  ts类型说明文件中对这个IArguments类型的解释
    // [index: number]: any;
    // length: number;
    // callee: Function;
    //    }
}


// 元组:
let tuple1:[number,string,boolean]=[1,'编译',true]


// 进阶 区别 函数模拟请求接口返回的内容
function useFetch(){
    const response:string='taotao';
    const age:number=28;
    // return [response,age] as const;
    return tuplify(response,age);
}

// 定义一个函数用来强返回的结果,转化为元组,方便后续的解构
// T extends unknown[] T继承一个不知道类型的数组
// 传入的参数是T类型的
// 返回的结果也是类型的
function tuplify<T extends unknown[]>(...elements:T):T {
    return elements
}
// 当我们结构时,发现age的类型是age: string | number,并不是单纯的number类型
// 这该如何解决呢? 使用as const  类型断言  或者定义一个函数,将返回的结果转化为元组返回
const [response,age]=useFetch()
console.log(age)  //这个时候age: number  age就是number类型的
3.ts中的函数

我们从以下几个方面如下,请看代码:

// ts中的函数
// 1.不要和es6中的函数混淆
// :(x:number,y:number)=>number  定义一个函数的类型,包括参数和返回结果的类型
// 后面是一个函数的定义 不能混淆了
let  myFunc:(x:number,y:number)=>number =function(x:number,y:number):number{
 return x+y
}  

// 2.函数默认值 建议是写在参数的后面,写在前面也不报错
function allName(firstName:string,lastName:string='taotao'):string{
    return firstName+lastName
}

// 3.剩余参数
function getArr(array:any[],...items:any[]){
    items.forEach(item=>{
        array.push(item)
    })
}
let a=[2,3]
getArr(a,4,5,6,7)
console.log('🍎',a) //🍎 [ 2, 3, 4, 5, 6, 7 ]


// 4.可选参数  必须要放到参数的后面,不然会报错的=>必选参数不能位于可选参数后。
function allName1(firstName:string,lastName?:string):string{
     if(lastName){
        return firstName+lastName
     }else{
        return firstName
     }
}
4.ts中的类
/**
 * 类  分为抽象类与普通类
 * 普通类就是我们平常接触到最多的类,可以被实例化,存在的意义是被实例化
 * 抽象类就是相当于是特殊的类,被abstrct修饰的类,不能被实例化,存在是为了被继承;
 * 一。两者的区别是:
 * 1.抽象类不能被实例化,普通类可以
 * 2.抽象类需要被abstract修饰,普通类不用
 * 3.抽象类和普通类都可以拥有普通成员和普通方法
 * 4.类都可以被继承或者继承别的类(包括抽象类和普通类)
 * 5.抽象类的子类必须重写抽象类中中的抽象方法
 * 
  
 * 二。方法修饰符   
 * public  共有的  任何地方都可以访问到
 * private 私有的  不能再类的外部使用
 * protected  保护的 子类中可以使用的
 * 
 * 3.
 */

abstract class Animal{
    abstract eat():void;  //抽象方法必须有继承类实现
    protected move():void{
        console.log('快速移动')
    }
}

class Dog extends Animal{
    constructor(){
        super()
    }
    eat(){   //抽象方法必须别重写,不然会报错
         this.move()
         console.log('吃骨头')
    }
}

const dog=new Dog()
dog.eat()    打印结果://快速移动
                      //吃骨头

其中私有属性的点 也就是private 与 #之间的区别是:可以看如下代码:

class Demo{
    private num:number
    // #num:number
    constructor(){
        // this. #num=20
        this.num=20
    }
}

const demo=new Demo()
console.log(demo.num)   //报:属性“num”为私有属性,只能在类“Demo”中访问

在package.json中新增命令看下编译的结果:"build":"tsc demos/demo.ts"

终端执行命令yarn build 生成demo.js文件代码如下:

var Demo = /** @class */ (function () {
    // #num:number
    function Demo() {
        // this. #num=20
        this.num = 20;
    }
    return Demo;
}());
var demo = new Demo();
console.log(demo.num);

我们使用#来创建私有变量

class Demo{
    // private num:number
    #num:number
    constructor(){
        this. #num=20
        // this.num=20
    }
}

const demo=new Demo()
console.log(demo.num)  //报:类型“Demo”上不存在属性“num”。

执行yarn build命令生成demo.js文件代码如下:

var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _Demo_num;
var Demo = /** @class */ (function () {
    function Demo() {
        // private num:number
        _Demo_num.set(this, void 0);
        __classPrivateFieldSet(this, _Demo_num, 20, "f");
        // this.num=20
    }
    return Demo;
}());
_Demo_num = new WeakMap();  //可以看到这个时候的私有变量变成了WeakMap
var demo = new Demo();
console.log(demo.num);
5.ts中的类型断言

类型断言可以用来手动指定一个值的类型 ,语法 :值 as 类型 或者<类型>值

  • 1 联合类型可以被断言成其中一个类型
  • 2 父类可以断言成子类
  • 3 任何类型可以断言成any
  • 4 any 可以断言成任何类型

实际代码如下:

// 类型断言
// 第一种方式 <>值
function getLength(str:number |string):number{
    // 这个时候需要做判断
    if((<string>str).length){
        return (<string>str).length
    }else{
        return str.toString().length
    }
}
// 第二种方式  as 类型
function getLength1(str:number |string):number{
    // 这个时候需要做判断
    if((str as string).length){
        return  (str as string).length
    }else{
        return str.toString().length
    }
}

// 实际项目中我们会使用type  别名来定义类型
type Name=string
type AllName=()=>string
type NameOrAll=Name | AllName
function getName(n:NameOrAll):Name{
//    这个地方判断
 if(typeof n ==='string'){
    return n
 }else{
    return n()
 }
}
6.ts中的泛型

泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性。

在我们不知道是什么类型的时候使用泛型

1.我们函数中使用泛型,有点类似函数的重载

函数的重载

// 函数的重载
function addRes(...rest:number[]):number;
function addRes(...rest:string[]):string;
function addRes(...rest:any[]){
    let first=rest[0]
    if(typeof first ==='number'){
        return rest.reduce((pre,cur)=>pre+cur)
    }
    if(typeof first==='string'){
        return rest.join('')
    }
}

console.log(addRes(1,2))  //3  //求和
console.log(addRes('a','b','c')) //abc  合并成字符串

函数中使用泛型,实现函数的重载

function func<T>(arg:T):T{//T就是表示不知道的类型,传入的参数是什么类型,返回的结果就是什么类型
   console.log(arg)  
   return arg
}
func(11)  //11  number的类型
func('taotao')  //taotao  string的类型 
func(()=>{console.log('111')})  //[Function (anonymous)]  函数的类型

2.使用接口来限制泛型的类型

// 我们改造上面的函数,我们像获取参数的长度,用泛型来实现
// 我们又不知道传入的参数是否有length属性,所以我们需要进行限制
// 使用接口来限制泛型的类型
interface LengthWise{
    length:number
}
function getLength<T extends LengthWise>(arg:T):T{
    console.log(arg.length)
    return arg
}
getLength('taotao')  //6 字符串的长度
getLength(1111)  //undefined  报错 number类型的是没有length属性的

3.类中使用泛型

// 类中使用泛型呢

class getNumber<T>{
   num:T | undefined;
//    constructor(num:T){
//     this.num=num
//    }
   add:((x:T,y:T)=>T)| undefined
}

const getNum=new getNumber<number>()
getNum.num=0
getNum.add=function(x,y){
    return x+y
}
console.log(getNum.add(20,40))  //60

4.其他的小知识点:

// 泛型接口

interface Func{
    <T>(value:T):void
}

const getData:Func=<T>(value:T):void=>{console.log(value)}
getData(1111) //1111

// 动态泛型

interface Bookmark{
    msg:string
}
//继承并且给默认值
class BookmarkService<T extends Bookmark = Bookmark>{
  items:T[]=[]
}
7.ts中的接口 interface

interface 与type 之间的区别

  • 相同点:
  • 1.都可以描述一个对象或者函数
  • 2.都允许进行扩展
  • 不同点:
  • 1.type 可以声明基本类型别名,联合类型,元组等
  • 2.typeof 获取实例的对象
  • 3.interface可以合并

interface使用建议 1.有关后台的接口 建议去使用interface 2.第三方的和开发SDK 比如 vue 3.正常的开发任务,使用type即可,方便直接

//模拟实现一个接口
interface PriceData{
    id:number,
    price:number,
    name:string
}
type PriceDataArray=PriceData[]  //定义的接口类型的数组类型
// 模拟接口的实现
function getPriceData(){
    return  new Promise<PriceDataArray>((resolve,reject)=>{
         fetch('url').then(function(response){
            return response.json()
         }).then(function(){
            const data:PriceDataArray=[]
            resolve(data)
         })
    })
}

getPriceData().then((data)=>{
    console.log(data[0].name)
})