# this

# this

采用 this 指代当前的对象类型。 在动态扩充对象方法时,利用 this 避免调用出错。

class FooC {
  // 注意此处的返回值申明,要求返回的 this 同时具有泛型对象的属性
  extend<T>(obj: {}): this & T {
    Object.assign(this, obj);
    // 类型断言避免返回类型 this 和 this & T 不匹配
    return this as this & T;
  }
  hi() {
    console.log('hi');
    return this;
  }
}
let fooC = new FooC();

fooC.hi();

fooC
  .extend({
    hello() {
      console.log();
    }
  })
  .hello(); // 报错, typescript 无法检测动态添加的方法,需要在 extend 显示申明扩充类型才可以

fooC
  .extend<{ bye(): void }>({
    bye() {
      console.log('bye');
    }
  })
  .hi()
  .bye(); // 合法,由于返回值类型为 this & { bye():void} 的交叉类型包含所有属性

阅读 this-typing (opens new window) 进一步理解。

也可在函数中申明 this,限定函数中 this 的使用。

// 显示申明函数的 this 为空类型
function noThisHi(this: void, name: string) {
  console.log(`Hi ${name}`);
}

// 注意函数调用时 this 不会被识别为参数,它只是限制 this 的使用
noThisHi('tom');

// 非法, 此处 call 的第一个参数为 this, TypeScript 识别到限制 this 修改
noThisHi.call({});

阅读 Specifying the type of this for functions (opens new window) 进一步理解。

# this 参数

在函数有时需要显示申明函数的 this 执行环境。 此时可以通过申明首个参数的 this 类型来实现。

function f1(this: void, foo: number) {
  // 报错,因为已显示申明 this 为空
  let b  = this.a;
  console.log(this, foo)
}
// 函数首个参数为 this 只是申明 this 在函数中的类型,并不代表实际参数
f1(12);

一个典型的使用是对回调标注,避免在回调函数中引用 this, 导致 js 中常见的 this 丢失问题。

type callback = (this: void, e: { type: string}) => void

function onChange(cb: callback) {
  cb({ type: 'change'})
}


onChange(function(a) {
  // 报错,无法在回调上使用 this.b 的属性
  let foo = this.b;
  console.log(a);
})

# fluent API

在 jquery 常见的链式调用方法,是通过返回对象的 this 实现的。 这种模式称为 fluent API (opens new window) 你可以通过将 返回对象声明为 this 类型实现推导和扩展。

class BasicCalculator {
   public constructor(protected value: number = 0) {}
   public currentValue(): number {
     return this.value;
   }
   
   // 注意这里申明返回值为 this 而非 BasicCalculator, 这样可以使返回对象兼容子类的扩展提示
   public add(operand: number): this {
     this.value += operand;
     return this;
   }
   public multiply(operand: number): this {
     this.value *= operand;
     return this;
   }
 }
 
 let v1 = new BasicCalculator(2).multiply(5).add(1).currentValue();

 class ScientificCalculator extends BasicCalculator {
   public constructor(value = 0) {
     super(value);
   }
   public sin() {
     this.value = Math.sin(this.value);
     return this;
   }
 }
 

v1 = new ScientificCalculator(2).multiply(5).sin().add(1).currentValue();

# 知识点

  • https://github.com/microsoft/TypeScript/pull/14141
  • https://github.com/microsoft/TypeScript/pull/6739
  • https://github.com/microsoft/TypeScript/issues/229