typescript进阶之路

MangoGoing      2022-02-12     383

关键词:

前言

反思了现在为什么越来越多的公司开始使用TypeScript开发应用,而大部分小厂还是热衷javascript,在多人协作的团队,代码的可读性、结构清晰、低耦合、易扩展显的尤为重要。

JavaScript

TypeScript 是 JavaScript的一个超集,它对JavaScript 做了一系列的增强,包括增加了静态类型接口泛型方法重载等。有了javascipt的功底,如果还有其他静态类型语言如java、dart等的基础,那么学习TypeScript也会更加容易上手。

JavaScript动态类型语言,在代码编译阶段不会对变量进行类型检测,从而增加了代码执行阶段的错误概率,这也是为什么前端程序员频繁使用console.log进行调试。在不同类型变量的赋值时,js还会自动进行类型转换,从而带来代码缺陷的可能性进一步增加。
JavaScript没有命名空间,需要手动创建命名空间,来进行模块化。并且,JavaScript 允许同名函数的重复定义,后定义的会覆盖之前定义的函数,这也给大型协同开发的项目带来很多麻烦。

TypeScript

简介

TypeScript静态类型语言,是一种面向对象的编程语言,它通过类型注解提供编译时的静态类型检查。
TypeScript解决javascript上述的一系列问题:包括
在代码编译阶段的变量的类型检测,会提前暴露潜在的类型错误问题。并且在代码执行阶段,不允许不同类型变量之间的赋值。
TypeScript的类型注解,赋予了 IDE 更强的代码补全能力,更人性化的代码提示,从而给开发带来更多的便利之处。
TypeScript 还增加了模块类型,自带命名空间,方便了大型应用的模块化开发。

特性

数据类型

  • 基础数据类型包括:Boolean、Number、String、Array、Enum、Any、Unknown、Tuple、Void、Null、Undefined、Never。

    • Enum 枚举:编程要避免使用硬编码,配置化的代码可以让代码更易维护。

    // 数字枚举在不设置默认值的情况下,默认第一个值为0,其他依次自增长
    enum TASK_STATUS {
    UNPLAYED,
    ONGOING,
    FINISHED,
    OBSOLETE
    }
    let status: TASK_STATUS = TASK_STATUS.UNPLAYED; // 0

    • Any 类型:不建议使用。Any 类型为顶层类型,所有类型都可以被视为 any 类型,使用 Any 也就等同于让 TypeScript 的类型校验机制失效。
    • Unknown 类型:优先考虑用 Unknown 代替 Any。Unknown 类型也是顶层类型,它可以接收任何类型,但它与 Any 的区别在于,它首次赋值后就确定了数据类型,不允许变量的数据类型进行二次变更。
    • Tuple 元组:支持数组内存储不同数据类型的元素。
    let tuple: [string, boolean];
    tuple= ["ghostwang", true];
    • Void:当函数没有返回值的场景下,通常将函数的返回值类型设置为 void。

类型注解

TypeScript 通过类型注解提供编译时的静态类型检查,在 : 冒号后面注明变量的类型即可。

const str: string = 'ghostwang';
const count: number = 10;

接口

面向对象编程,实现程序解耦的关键就是接口,它只定义属性和方法,不涉及任何具体的实现细节。接口是对实体或行为进行抽象,它是让复杂的逻辑抽离变的更加可扩展的关键。

interface Car {
  brand: string;
  getBrand(): String;
}
class Toyota implements Car {
  constructor(private name: string) {
    getBrand() {
      return '品牌: ' + name;
    }
  }
}

  • 类除了包括属性和方法、继承、getter 和 setter方法之外,还新增了私有字段。私有字段不能在包含的类之外访问,但是可以从一个公有的getter方法中拿到。
  • 属性和方法

    class CommonPerson {
      constructor(gender: string) {
        this.gender = gender;
      }
      static name: string = "ghostwang";
      gender: string;
      getName() {
        return this.name;
      }
      // 成员方法
      getGender() {
        return 'Gender: ' + this.gender;
      }
    }
    const p = new Person("男");
    p.name // 'ghostwang'
    p.getGender // '男'
  • getter 和 setter
class Person {
  private _name: string;
  get getName(): string {
    return this._name;
  }
  set setName(name: string) {
    this._name = name;
  }
}
let person = new Person('ghostwang');
person.getName(); // ghostwang
person.setName('mango');
console.log(person.getName()); // mango
  • 继承

    class Person {
      name: string;
      constructor(nameStr: string) {
        this.name = nameStr;
      }  
      walk(distance: number = 0) {
        console.log(`${this.name} walked ${distance}米`);
      }
      }
      class GhostWang extends Person {
        constructor(nameStr: string) {
          // 执行基类的构造函数,把参数传进去
          super(nameStr);
        } 
        walk(distance = 5) {
          super.walk(distance);
        }
      }
    const mongo = new GhostWang('mongo');
    mongo.move(); // 输出:'mongo walked 5米'
  • 私有字段

    • 私有字段以 # 字符开头。私有字段不能在包含的类之外访问。
    class Person {
      #name: string;
      constructor(name: string) {
        this.#name = name;
      }
      hello() {
        console.log(`${this.#name} say hello!`);
      }
    }
    let person = new Person('ghostwang');
    person.#name;   // 报错

    泛型

    使用泛型来创建的组件可复用和易扩展性要更好,因为泛型会保留参数类型。泛型可以应用于接口、类、变量:

  • 泛型接口

    interface identityFn<T> {
      (arg: T): T;
    }
  • 泛型类

    class GenericNumber<T> {
      zeroValue: T;
      add: (x: T, y: T) => T;
    }
    let myGenericNumber = new GenericNumber<number>();
    myGenericNumber.zeroValue = 0;
    myGenericNumber.add = function (x, y) {
      return x + y;
    };
  • 泛型变量

使用大写字母 A-Z 定义的类型变量都属于泛型,常见泛型变量如下:

  • T(Type):表示一个 TypeScript 类型
  • K(Key):表示对象中的键类型
  • V(Value):表示对象中的值类型
  • E(Element):表示元素类型

交叉类型

交叉类型就是将多个类型合并为一个类型。通过 & 运算符定义。如下示例中,将 Person 类型和 Company 类型合并后,生成了新的类型 Staff,该类型同时具备这两种类型的所有成员。

interface Person {
  name: string;
  gender: string;
  }
  interface Company {
  companyName: string;
  }
  type Staff = Person & Company;
  const staff: Staff = {
  name: 'ghostwang',
  gender: 'female',
  companyName: 'hz'
};

联合类型

联合类型就是由具有或关系的多个类型组合而成,只要满足其中一个类型即可。通过 | 运算符定义。如下示例中,函数的入参为 string 或 number 类型即可。

function fn(param: string | number): void {
  console.log("This is the union type");
}

类型保护

类型保护就是在我们已经识别到当前数据是某种数据类型的情况下,安全的调用这个数据类型对应的属性和方法。常用的类型保护包括 in 类型保护、typeof 类型保护、instanceof 类型保护和 自定义 类型保护。具体见以下示例:

  • in 类型保护

    interface Person {
      name: string;
      gender: string;
    }
    interface Employee {
      name: string;
      company: string;
    }
    type UnknownStaff = Person | Employee;
    function getInfo(staff: UnknownStaff) {
      if ("gender" in staff) {
        console.log("Person info");
      }
      if ("company" in staff) {
        console.log("Employee info");
      }
    }
  • typeof 类型保护

    function processData(param: string | number): unknown {
        if (typeof param === 'string') {
          return param.toUpperCase()
      }
      return param;
    }
  • instanceof 类型保护:和 typeof 类型用法相似,它主要是用来判断是否是一个类的对象或者继承对象的。

    function processData(param: Date | RegExp): unknown {
        if (param instanceof Date) {
          return param.getTime();
      }
      return param;
    }
  • 自定义 类型保护:通过类型谓词 parameterName is Type 来实现自定义类型保护。如下示例,实现了接口的请求参数的类型保护。

    interface ReqParams {
        url: string;
         onSuccess?: () => void;
         onError?: () => void;
    }
    // 检测 request 对象包含参数符合要求的情况下,才返回 url
    function validReqParams(request: unknown): request is ReqParams {
        return request && request.url
    }

    开发小技巧

  • 需要连续判断某个对象里面是否存在某个深层次的属性,可以使用 ?.

    if(result && result.data && result.data.list) // JS
    if(result?.data?.list) // TS
  • 联合判断是否为空值,可以使用 ??

    let temp = (val !== null && val !== void 0 ? val : '1'); // JS
    let temp = val ?? '1'; // TS
  • 不要完全依赖于类型检查,必要时还是需要编写兜底的防御性代码。

因为类型报错不会影响代码生成和执行,所以原则上还是会存在 fn('str') 调用的可能性,所以需要 default 进行兜底的防御性代码。

function fn(value:boolean){
    switch(value){
      case true: 
        console.log('true');
      break;
    case false: 
      console.log('false');
      break;
    default: 
      console.log('dead code');
  }
}
  • 对于函数,要严格控制返回值的类型.
    // 推荐写法

    function getLocalStorage<T>(key: string): T | null {
      const str = window.localStorage.getItem(key);
      return str ? JSON.parse(str) : null;
    }
    const data = getLocalStorage<DataType>("USER_KEY");
  • 利用 new() 实现工厂模式

TypeScript 语法实现工厂模式很简单,只需先定义一个函数,并声明一个构造函数的类型参数,然后在函数体里面返回 c 这个类构造出来的对象即可。以下示例中,工厂函数构造出来的是 T 类型的对象。

function create<T>(c: { new(): T }): T {
    return new c();
}
class Test {
  constructor() {
  }
}
create(Test);
  • 优先考虑使用 Unknown 类型而非 Any
  • 使用 readonly 标记入参,保证参数不会在函数内被修改

    function fn(arr:readonly number[] ){
      let sum=0, num = 0;
      while((num = arr.pop()) !== undefined){
        sum += num;
      }
      return sum;
    }
  • 使用 Enum 维护常量表,实现更安全的类型检查

    // 使用 const enum 维护常量配置
    const enum TASK_STATUS {
      UNPLAYED,
      ONGOING,
      FINISHED,
      OBSOLETE
    }
    function handleTask (status: TASK_STATUS): void;
    handleTask(TASK_STATUS.FINISHED)

typescript入门学习之路(代码片段)

TypeScript学习之路TypeScript学习之路安装typescript环境typescript起步typescript开发工具vscode自动编译.ts文件typescript中的数据类型typescript中的函数typescript中类的定义继承javascript中的继承typescript中的继承typescript类的静态属性和静态方法t... 查看详情

进阶typescript

TypeScript1.类型定义:boolean:consttest:boolean=falsenumber:consttest:numberstring:consttest:string=''Array:consttest:number[]=[1,2,3]consttest:Array<number>=[1,2,3]//Array<number>泛型语法Enum枚举类 查看详情

ssh进阶之路hibernate映射——一对一单向关联映射

【SSH进阶之路】Hibernate基本原理(一),小编介绍了Hibernate的基本原理以及它的核心,採用对象化的思维操作关系型数据库。【SSH进阶之路】Hibernate搭建开发环境+简单实例(二),小编搭建了基本Hibernate的开发环境。并做了一... 查看详情

css进阶之路

...面主要引用http://www.cnblogs.com/wangfupeng1988/tag/css知多少/CSS进阶笔记:一、学习CSS的三个突破点1.浏览器如何加载和解析CSS——CSS的5个来源;2.CSS和html如何结合起来——选择器;3.CSS能控制那些显示方式——盒子模式、浮动、定位、... 查看详情

《前端之路》-typescript函数篇

...数和函数表达式(匿名函数)这门两种。那么同样的,在TypeScript中,函数的定义是什么样子的呢?1-1命名函数这里需要注意的一点是:viod类型,是函数不返回任何类型数据TypeScript语法functionfunc1():string{ 查看详情

ssh进阶之路

 【SSH进阶之路】Hibernate基本原理(一)      在开始学Hibernate之前,一直就有人说:Hibernate并不难,无非是对JDBC进一步封装。一句不难,难道是真的不难还是眼高手低?   如果只是停留在使... 查看详情

小白的进阶之路9

---恢复内容开始---今天开始学习spring的事物管理先写一个转账的例子:动手之前,先把逻辑搞清楚:持久层:    AccountDao()------》接口      publicvoidin(Stringin,doublemoney);      publicvoidout(Stringout,double... 查看详情

ssh进阶之路hibernate基本映射

【SSH进阶之路】Hibernate基本原理(一),小编介绍了Hibernate的基本原理以及它的核心。採用对象化的思维操作关系型数据库。【SSH进阶之路】Hibernate搭建开发环境+简单实例(二)。小编搭建了基本Hibernate的开发环境,并做了一... 查看详情

ssh进阶之路hibernate基本映射

【SSH进阶之路】Hibernate基本原理(一),小编介绍了Hibernate的基本原理以及它的核心。採用对象化的思维操作关系型数据库。【SSH进阶之路】Hibernate搭建开发环境+简单实例(二),小编搭建了基本Hibernate的开发环境。并做了一... 查看详情

java进阶之路-java中的threadpoolexecutor

参考:深入理解ThreadPoolExecutor 查看详情

java进阶之路-java中的位操作

「WTF系列」深入Java中的位操作 查看详情

go语言的进阶之路-面向对象编程

                                   GO语言的进阶之路- 查看详情

前端进阶-typescript类型声明文件详解及使用说明(代码片段)

...页👉🏻蜡笔雏田学代码专栏链接👉🏻【TypeScript专栏】前三篇文章讲解了TypeScript的一些高级类型详细内容请阅读如下:🔽【前端进阶】-TypeScript高级类型|泛型约束、泛型接口、泛型工具类型【前端进阶... 查看详情

jvm进阶之路十二:字节码指令

...章里,我们进一步学习字节码的相关指令。在前面的【JVM进阶之路】三:探究虚拟机对象里,提到了对象的初始化过程,对象初始化用的是new指令——这就是字节码指令。在【JVM进阶之路】十一:Class文件结构中已经学习了JVM字... 查看详情

进阶之路(基础篇)-007脉冲宽度测量

1/*********************************2代码功能:Pulse脉冲宽度测量3使用函数:4pulseIn(引脚号,脉冲响应电平,最大响应时间(微秒));5创作时间:2016*10*086作者邮箱:[email protected]7********************************/8intkeyPin=2;9voidsetup()10{11pi 查看详情

菜鸡进阶之路

 记录一下自己一点大学的经历2018.10山东省高校机器人大赛省赛二等奖2018.12青岛大学电子设计竞赛校赛一等奖2018.12青岛大学第一届程序设计大赛(acm团体赛)冠军2019.3第十届蓝桥杯省赛(个人赛)省赛一等奖2019.4计科院图灵... 查看详情

javascript进阶之路1

路漫漫其修远兮,吾将秃了头依然不见大明湖畔的夏雨荷。当年我还是个javascript小白,项目经理分下来一个验证表单功能的任务,内容不多,仅需要验证用户名、邮箱、密码等。我一看soeasy,于是便写下几个函数。1functioncheckNam... 查看详情

小白的进阶之路1

在登陆的页面中,需要一个表单,<formaction="\goods\loginServlet" method="post">为什么要有这个servlet?答:需要将这个用户填好的表单交给其他的东西,比如服务器,或者servlet来继续处理或响应。用户名:<input type="text"nam... 查看详情