Skip to content

Latest commit

 

History

History
747 lines (673 loc) · 21.2 KB

10.typeScript.md

File metadata and controls

747 lines (673 loc) · 21.2 KB

typeScript

简介

安装使用

  • 常规安装运行
    • npm i typescript -g 安装
    • tsc -v 查看当前 typescript 版本
    • tsc -init 初始化项目
    • tsc -w 编译成js
    • 1.先运行 ts文件 将ts 文件进行编译 得到 js 文件 tsc ts文件
    • 2.执行 js 文件 node js文件
  • 简化运行
    • npm i -g ts-node 简化运行
    • ts-node 文件名 运行

类型注解

  • 为变量添加类型约束
  • 约定了是什么类型,就只能给变量 赋值该类型的值,否则会报错 (约定类型 写小写 大写用于接口)
  • let str:string='必须是字符串'
  • let srt = "1"; //不指定类型 会根据初始赋值来推导出变量的类型 以后str的类型不能改变
  • const num = 1; // 常量不能改变指向(不能被修改),所以它的值 就是它的类型

Ts 类型

  • 基础类型 number string boolean null undefined Symbol
  • 对象类型 object array function
  • TS 新增类型
    • 联合类型、交叉类型 、 自定义类型(类型别名) 、接口 、元组 、字面量类型 、枚举 、void 、any

原始数据类型 (string number boolean undefined null Symbol)

  let strings: string = "最好的";
  let numbers: number = 2019;
  let booleans: boolean = true;
  let undefineds: undefined = undefined;
  let nulls: null = null;
  let symbols: symbol = Symbol("唯一的");

非原始类型 object Object {} 三种形式

  • object 不包含基础数据类型 (例如string、number 等)
  • Object 包含基础数据类型 (注意区分 object 首字母大小写)
  • {}===Object
    // object 不包含基础数据类型
    let obj: object = { a: 1 };
    let arr: object = [1];
    
    // Object 包含基础数据类型
    let obj2: Object = { a: 1 };
    let arr2: Object = [1];
    let string1: Object = "张三";
    let number1: Object = 123;
    let boolean1: Object = true;
    
    // {} 等同于 Object
    let obj3: {} = { a: 1 };
    let arr3: {} = [1];
    let string2: {} = "张三";
    let number2: {} = 123;
    let boolean2: {} = true;
  • array 数组两种写法
    • 1.let a:Number[]=[1,2,3,4] 在类型约束后 添加中括号 表示 数组中的子级只能是当前约束类型 (推荐使用)
    • 2.let b:Array<String> = ['a','b','c'] 先约束为数组类型 在约束子级的类型,泛型 类型参数化(不推荐使用)

交叉类型 & (与)

  • & 用于组合多个类型为一个类型 (类似于接口继承 extends)
  • demo
      interface person1 { name: string }
      interface person2 { age: number }
      type person3 = person1 & person2
      let p: person3 = {
        name: '张三',
        age: 18
      }
    
    // 表示 所有指定的类型都要存在 缺一不可 (age 为指定的常量)
    let obj: { name: string; age: 18 } & { sex: string };
    obj = { name: "张三", age: 18, sex: "男" };
  • 交叉类型和接口继承类型的不同点
    • 相同点:都可以实现对象类型的组合
    • 不同点:对于同名属性之间 处理类型冲突的方式不同

联合类型 | (或)

  • | 使用|(竖线) 由两个或多个其他类型组成的类型 表示可以是这些类型中的任意一种
  • let a:(Number|String|Boolean)[]=[1,'string',true,234] 可以是联合类型中的任意类型
  // 为 数字 或 字符串
  let numAndsrt: number | string = 10;
  
  // 为 数字 或 等于 "小八嘎"
  let numAndsrt2: number | "小八嘎" = "小八嘎";
  
  // obj 为  { a: 1 } 或者 { b: 2 } 也可以同时为 { a: 1, b: 2 }
  let obj: { a: 1 } | { b: 2 } = { a: 1 };
  obj = { a: 1, b: 2 };

any 类型 不推荐使用 使用any类型后 将失去TS类型保护的优势

  • 当类型为any 时 可以对该值进行任意操作 并且不会有代码提示
  • 最多临时使用any来代替复杂类型 后面进行更正
  • let names:any='张三'; names=123; 使用any类型后 失去ts类型保护
let msg: any = 1;
msg = "小八嘎";
msg = true;
// msg.toFixed(2) //使用当前数据类型不具有的方法 不会 提示报错

unknown

let msg2: unknown = 1;
msg2 = "小八嘎";
msg2 = true;
// msg2.toFixed(2); //使用当前数据类型不具有的方法 会 提示报错

interface 接口

  • 使用 interface 声明创建 创建后使用接口名称作为变量的类型
  • 当一个对象类型被多次使用时 使用接口 interface 来描述对象的类型 达到复用的目的
  • demo
    // 对象
    interface ObjIft {
      // 属性名:值的类型
      name: string;
      age: number;
      sex: string;
    }
    let obj: ObjIft;
    obj = {
      name: "张三",
      age: 18,
      sex: "男",
    };
    
    
    // 数组
    interface ArrIft {
      // [idx: number] 下标类型: 值类型;
      [idx: number]: number | string;
    }
    let arr: ArrIft;
    arr = [1, "2", 3];
    
    
    // 函数
    interface FnItf {
      // (形参及类型):返回值类型
      (value1: string, value2: number): void;
    }
    let Fn: FnItf = (value1: string, value2: number) => {};
    Fn("小八嘎", 27);
    
    
    
    //如下待处理
      interface PersonS{ name: string; age: number; sayHi(abc: string): void; other: () => void }
    
      et person1: PersonS = {
       name:'张三',
       age:18,
       sayHi(i){
         console.log('类型一',i);
       },
       other() {
         console.log('类型二');
       }
      }
      erson1.sayHi('参数')

继承 extends

  • 将公共的属性和方法抽离出来 通过继承来实现复用
    interface person1 { name: string; age:number}
    interface person2 extends person1{sex:string}
    let person: person2 = {
      sex: "男",
      name: "张三",
      age: 18
    }
    interface NameIft {
    name: string;
    }
    
    
    // 继承 extends
    interface Ageift {
      age: number;
    }
    interface PersonIft extends NameIft, Ageift {
      sex: string;
    }
    let person: PersonIft;
    person = {
      name: "张三",
      age: 18,
      sex: "",
    };

修饰 同名、缺省、只读

  // 同名
  // 接口可以同名 定义参数类型会合并 但类型不同会报错
  interface withItf {
    name: string;
  }
  interface withItf {
    age: number;
  }
  let withs: withItf = {
    name: "张三",
    age: 18,
  };

  // 缺省 xxx? : 类型 表示可以缺省这个属性 定义数据时可以不写
  interface lack {
    name: string;
  }
  interface lack {
    age?: number;
  }
  let lacks: lack = {
    name: "张三",
  };

  // 只读 
  interface readOnlys {
    readonly numbers: number;
  }
  let rdy: readOnlys = { numbers: 10 };
  // rdy.numbers = 11; //报错 不能修改为只读的属性

type 类型别名 (自定义类型)

  • 使用 type 关键字来创建类型别名 创建后直接使用该类型别名作为变量名的类型注解
  • demo
    //场景 当 如下约束出现多次时 每次都要单独设置 约束的集合
      let a:(Number|String|Boolean)[]=[1,'string',true,234]
    //简化
      type Arrays=(Number|String|Boolean)[]
            let array1:Arrays=[1,'string',true,234]
            let array2:Arrays=[234,'string',1,true]
    
    type AA = number | string
    let abc: AA = 123
    abc = 'aaa'
    
    <!-- ---- -->
    // 自定义一个类型
    type strAndnum = string | number;
    
    let sn: strAndnum = "字符串或者数字";
    
    // 自定义一个对象类型
    type objType = { name: string; age: number & 2 };
    let obj: objType = {
      name: "张三",
      age: 2,
    };

函数类型

  • 函数的类型实际上指的是 函数参数和返回值的类型

  • 1.单独指定参数和返回值的类型

      //函数声明试 方式
      function add(num1:number,num2:number):number{
        return num1+num2
      }
      let a=add(5,6) 
      console.log(a); //11
    
      //函数表达式 方式
      const add=(num1:number,num2:number):number=>{
        return num1+num2
      }
      let a=add(5,6) 
      console.log(a); //11
  • 2.同时指定参数和返回值的类型 ```ts // 只适用于 函数表达式 方式 const add=(num1:number,num2:number):number=>(num1,num2)=>{ return num1+num2 } let a=add(5,6) console.log(a); //11

      // 接口形式
      interface FnItf {
        (p: string): number;
      }
    
      let fn: FnItf = (p: string) => {
        return 1;
      };
      fn("");
    
      // 自定义别名类型 形式
    
      type FnType = (p: string) => void;
      let fn2 = (p: string): void => {};
      fn2("");
            ```
  • 函数参数

      // 函数参数的写法
    
      // 不写为默认值
      function test1(a: number, b: number = 3) {
        console.log(a, b);
      }
      test1(1);
    
      // 可以不写
      function test2(a: number, b?: number) {
        console.log(a, b);
      }
      test2(1);
    
      // 结构剩余参数
      function test3(a: number, b: number, ...arr: number[]) {
        console.log(a, b, arr);
      }
      test3(1, 2, 3, 4, 5);

  • void 当函数没有返回值 函数的返回值类型就是 void

    • function a(){} === function a():void{} 类型为 void
    • function str(name:string):void{console.log('单纯打印没有返回值') }
  • 可选参数

    • 可选参数 :在参数名后添加 表示可传可不传
    • 可选参数 只能出现在参数列表的最后面 可选参数后 决定不能出现 必选参数
    • demo
        const add=(num1?:number,num2?:number):void=>{
        console.log(`数字一:${num1}数字二:${num2}`);
        }
        add() //数字一:undefined数字二:undefined
        add(123) //字一:123数字二:undefined
        add(123,456) //数字一:123数字二:456
  • 对象类型

    • TS中对象类型就是在描述对象的结构 (有什么类型的属性和方法)
    • demo
      let person: { name: string; age: number; sayHi(abc: string): void; other:()=>void}={
      name:'张三',
      age:18,
      sayHi(i){
        console.log('类型一',i);
      },
      other() {
        console.log('类型二');
      }
      }
      person.sayHi('参数')
    • 对象可选属性 (可选属性同函数可选参数 )
      • demo
        function myAxios(config:{url:string;method?:string}){}
        myAxios({
          url:'123456'
        })
  • 元组 (tuple)

    • 元组类型是另一种类型的数组
    • 使用场景: 当明确的知道包含多少个元素 以及特定索引对应的类型
    • 元组类型可以确切的标记出有多少个元素 以及每个元素的类型
    • let person: [string, number, string] = ['张三', 18,'男']
      • 不能多不能少 类型必须一一对应
  • 类型推论

      1. 声明变量初始化时
      • let str='是字符串' 声明变量并立即初始化值 才可以进行省略 写类型
      • let a; a = 18; a='789'; 进行声明后 没有立即初始化值 不可以省略 导致ts无法确切规定类型
      1. 决定函数返回值时
      • demo
          function add(num1:number,num2:number):number{
            return num1+num2
          }
          //省略后
          function add(num1:number,num2:number){
            return num1+num2
          }
  • 类型断言 as

    • 使用 as 关键字 实现类型断言
    • 关键字as 后面的类型 是一个更加具体的类型
    • demo
        //,没使用as 进行类型断言 
        //getElementById方法返回的值类型是 HTMLelement 该类型不包含a标签特有的 href 等属性
        //这个类型太宽泛 不具体 无法操作 href 等 标签特有的属性或方法
        //使用 as 类型断言 指定更加具体的类型 
        let AA = document.getElementById('baidu')
        console.log(AA.href);
      
      
        let AA = document.getElementById('baidu') as HTMLAnchorElement
        console.log(AA.href);
      
        AA.onclick = (() => {
          console.log(1);
        })
  • 字面量类型

    • 场景 字面量类型配合类型一起使用 用来表示一组明确的可选值列表
    • demo
      let str1='hello ts' // let str1: string
      const str2='hello ts'//const str2: "hello ts"  等同于  const str3:'hello ts'='hello ts'
      
      //直接规定 类型对应的值
      let age:19=10//error:不能将类型“10”分配给类型“19”  
      let age2:19=19
      function aa(direction: 'up' | 'down' | 'left' | 'right') { }
      aa('up')
  • typeof 操作符

    • TS中的typeof 可以在类型上下文中引用变量或属性的类型 (或类型查询)
    • 可以用于 根据已有变量的值 获取该值的类型 来简化类型书写
    • typeof 只能用来查询变量或属性的类型 无法查询其他形式的类型 (例如函数调用的类型)
    • demo
        let person = { name: '张三', age: 18 }
        // function onePerson(point: { name: string; age:number}){}
      
        // 使用typeof
        function onePerson(point: typeof person){}
        onePerson({ name: "李四",age:19})

enum 枚举

-(不赋值的情况下) 枚举成员的值 默认为 从0 开始自增

  • 当给 一个枚举对象 赋值为数字 后面的枚举对象如果没值 依然会自动递增
  • 字符串枚举没有自增长行为 因此每个字符串枚举成员 必须有初始值
    • 字符串如果不是最后一个 那么必须全部有初始值
  • demo
 enum Direction{ up, down, left, right }
 function changeDirection(direction: Direction) {
   console.log(Direction.down);
 }

 // 自动递增
 enum Direction{
 up=10,//10
 down, //11
 left, //12
 right //13
 }

 //枚举对象为 字符串时

 enum Direction{
 up,
 down, 
 left, 
 right='a' 
 }

 // 枚举 不是用来定义类型的 用于列举数据用的
 enum StatusCode {
   success = 200,
   paramsError = 400,
   serverError = 500,
 }

 let code: string | number = 200;
 if (code === StatusCode.success) {
   console.log("成功");
 } else if (code === StatusCode.paramsError) {
   console.log("失败,请求参数问题");
 } else if (code === StatusCode.serverError) {
   console.log("失败, 服务器问题");
 }

 enum StatusCode2 {
   success,
   paramsError = 400,
   serverError,
 }

 console.log(
   StatusCode2.success,
   StatusCode2.paramsError,
   StatusCode2.serverError
 );
 // 输出结果:0 400 401
 // 会自动递增

class 类

  • TS中的class 不仅提供了 class 的语法功能 也作为一种类型存在

  • 构造函数

    • 成员初始化后 才可以通过this.xxx 来访问实例成员
    • 需要为构造函数指定类型注解 否则会被隐式推断为 any
    • 构造函数不需要返回值类型
    • demo

    class person { age: number name:string constructor(age: number, name: string) { this.age = age this.name=name } } const p=new person(18,'张三') console.log(p.name,p.age); //张三 18

    //------------- class Person { // 定义属性前 应该先声明这个属性的类型,也可以同时设置默认值 myName: string = "默认值"; constructor(n: string) { this.myName = n; } getName() { return this.myName; } } let p = new Person("张三"); console.log(p.myName); console.log(p.getName);

    // 创建一个类 ts 会自动生成一个对应的接口 interface

    // 以上 class Person 相当于 如下 Person 接口 /* interface Person { myName: string; getName: () => string; } */

    // 使用class Person 自动生成的接口 let obj: Person = { myName: "李四", getName() { return "abc"; }, };

  • 继承 -方式1 extends 继承父类

    • demo
      class person1 {
      jineng1(){console.log('我会唱歌我会跳舞');
      }
      }
      class person2 extends person1{
        jineng2(){console.log('你放屁');
        }
      }
      const AA = new person2()
      AA.jineng1()
      AA.jineng2()

    -方式2 implements 实现接口 js中只有extend implements是TS的

    • 通过 implements 关键字并配合 interface 让class 实现接口
    • demo
        interface person1 {
        jineng1(): void
        name:string
        }
      
        class person2 implements person1{
          jineng1(){console.log('我是一个粉刷匠');
          }
          name='张三'
        }
        const AA = new person2()
        AA.jineng1()
        console.log(AA.name);
  • 类成员可见性

    • public 公有的 公有成员可以被任何地方访问 (默认为可见可以省略)
    • protected 受保护的 仅对齐声明所在类和子类中(非实例对象)可见
    • private 私有的 只能在当前类中可见 对实例对象以及子类不可见
      • demo
          class person1 {
          public  jineng1() {
              console.log('我会唱歌');
            }
          protected  jineng2() {
              console.log('我会跳舞');
            }
          private  jineng3() {
              console.log('你牛逼');
            }
          }
          class person2 extends person1{
            jineng4() {
              console.log('你放屁');
              this.jineng2()
            }
          }
          const AA = new person2()
          AA.jineng1()
          // AA.jineng2() //属性“jineng2”受保护,只能在类“person1”及其子类中访问。
          // AA.jineng3() //属性“jineng3”为私有属性,只能在类“person1”中访问
    • readonly 只读
      • 用于防止在构造函数之外对属性进行赋值
      • 只能修饰属性 不能修饰方法
      • 接口 或者{} 表示的对象类型 也可以使用 readonly
      • 只要是 readonly 修饰的属性 必须手动提供明确的类型
      • demo
          class preson{
          readonly age: number = 18
          constructor(age: number) {
            this.age = age
            this.age=19
          }
          }
          const p = new preson(20)
          console.log(p);
          // p.age=30 //error 无法分配到 "age" ,因为它是只读属性
  • static 静态属性(成员)

    • 访问修改 只能通过类去操作

泛型

  • 泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。
  • 泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用
  • 简化调用泛型函数 在调用泛型函数时 可以省略 <类型>
  • demo
    //需求:创建一个val 函数,传入什么数据就返回该数据本身(也就是说,参数和返回值类型相同)
    function val<T>(value: T): T {
    return value
    }
    const v = val<number>(123) //指定类型 并传入指定数据
    //进行简化
    const v = val(123) //指定类型 并传入指定数据
    
    
    
    // 传入不同类型值 就需要先定义好 导致代码冗余不利于维护
    function fn(n: number | string): number | string {
      return n;
    }
    fn(100);
    fn("abc");
    
    // 通过泛型 解决 <T> 类似于类型变量 在传递时在确定是什么类型
    // <T> 常规使用 T 是一个标识符可以自定义
    function fn2<T>(n: T): T {
      return n;
    }
    fn2<number>(100);
    fn2<string>("abc");
    
    // 多个参数情况
    function fn3<T, G>(n: T, m: G): T {
      return n;
    }
    fn3<number, string>(100, "123");
    fn3<string, boolean>("abc", true);
    
    
    
    // 泛型在类型别名上
    type ObjType<A, B> = {
      name: A;
      getName: () => B;
    };
    let obj: ObjType<string, number> = {
      name: "",
      getName() {
        return 123;
      },
    };
    
    // 泛型在接口上
    // 可以设置默认类型 不传递时使用定义的默认类型
    interface PersonItf<A, B, C = boolean> {
      name: A;
      age: B;
      sex: C;
    }
    let objs: PersonItf<string, number> = {
      name: "张三",
      age: 18,
      sex: false,
    };

extends 泛型约束 规定只能接收的类型

  type strOrnum = string | number;
  interface PersonItf2<A extends strOrnum, B> {
    name: A;
    age: B;
  }
  let objss: PersonItf2<string, number> = {
    name: "张三", //只能传递 string 和 number 类型 数据
    age: 18,
  };

兼容性 待解决

  • 有版本问题 待解决
  • 类型兼容性 class
    • TS采用的是结构化类型系统 类型检查关注的是值所具有的形状。在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型
    • 成员多的可以赋值给成员少的 (反之则不能)
    • demo
        class person1 { a: number; b: number }
        class person2 { a: number; b: number }
        const p: person1 = new person2()
      
        //非相同成员情况
        class person1 { a: number; b: number }
        class person2 { a: number; b: number; c: number }
        const p: person1 = new person2()
  • 接口兼容性 interface
    • 接口之间的兼容性类似于 class 并且class 和 interface 之间也可以兼容
    • demo
      interface person2 {
      a: number
      b: number
      }
      class person3 { a: number; b: number; c: number }
      let p3:person2=new person3()

other

interface接口 和 type类型别名 的区别

  • 相同点: 都可以给对象指定类型
  • 不同点:
    • 1.接口 只能为对象指定类型 ,类型别名 可以为任意类型指定别名
    • 2.类型别名不支持重复定义 ,接口可以