前言

反思了当初为什么越来越多的公司开始应用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(); // ghostwangperson.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 对象蕴含参数符合要求的状况下,才返回 urlfunction validReqParams(request: unknown): request is ReqParams {    return request && request.url}

    开发小技巧

  • 须要间断判断某个对象外面是否存在某个深层次的属性,能够应用 ?.

    if(result && result.data && result.data.list) // JSif(result?.data?.list) // TS
  • 联结判断是否为空值,能够应用 ??

    let temp = (val !== null && val !== void 0 ? val : '1'); // JSlet 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)