聊一聊Typescript中与this相关的类型定义

9,441 阅读5分钟

    从本文开始,会陆续介绍一些typescript的使用,由浅入深,本文主要介绍一下Typescript中的this用法以及与this相关的内置函数。

  • Typescript的函数中声明this
  • Typescript中箭头函数和this声明
  • Typescript与this相关的内置函数

本文来自我的博客:github.com/fortheallli…

一、Typescript的函数中声明this

(1)函数中的this声明

     为了说明在typescript中this的声明方法,我们首先需要更改tsconfig.json的配置:

"compilerOptions": {
   ...
   "noImplicitThis": true
}

    默认情况下,如果ts没有this对象类型声明,this是自动隐式定义。如果noImplicitThis设置为true,此时不允许this上下文隐式定义,如果使用了没有声明过的this对象就会报错,举例来说(当设置noImplicitThis:true):

    function test() {
      const { a } = this;
      console.log(a);
    }
    test.call({ a: 1 });; //err 'this' implicitly has type 'any' because it does not have a type annotation.  TS2683

本文主要是为了介绍如何在typescript中声明this,因此需要将noImplicitThis设置为true,不允许隐式定义this,必须显式声明this。

简单来说,默认情况下可以理解成typescript将函数中的this as any,而oImplicitThis:true的情况下,必须去声明this的类型,才能在函数或者对象中使用this.

    我们来看一个简单的this的声明:

    function test(this:{a:number}}) {
      const { a } = this;
      console.log(a);
    }
    test.call({ a: 1 });; 

    上述我们在test函数的第一个参数中声明了this的类型,就不会报错。值得注意的是,this必须声明在函数参数声明中的第一个,且this在函数参数中的声明,不作为形参和实参,因此一下两种方式都是错误的:

  • this没有在函数第一个参数中声明
    function test(b: number, this: { a: number }) {
      const {a} = this;
      console.log(a);
    }
    test.call({ a: 1 });// error A 'this' parameter must be the first parameter. TS2680
  • 错误的调用了this声明的函数
    function test(this: { a: number }) {
      const {a} = this;
      console.log(a);
    }
    test({ a: 1 }); // 直接以传参数的形式调用test,Expected 0 arguments, but got 1.  TS2554

(2)回调函数中this声明

    除了在函数参数中可以定义this的类型外,在回调函数中也可以声明回调函数中的this的类型,这个听起来比较拗口,我们直接来看例子:


interface UIElement {
  addClickListener(onclick: (this: void, e: Event) => void): void;
}
class Handler {
  info: string;
  onClickBad(this: Handler, e: Event) {
    // oops, used `this` here. using this callback would crash at runtime
    this.info = e.message;
  }
}
const uiElement: UIElement = {
  addClickListener: () => {},
};
const h = new Handler('hello');
uiElement.addClickListener(h.onClickBad); // error Argument of type '(this: Handler, e: Event) => void' is not assignable to parameter of type '(this: void, e: Event) => void'.
  The 'this' types of each signature are incompatible.
    Type 'void' is not assignable to type 'Handler'.  TS2345


上述的例子中,在uiElement.addClickListener(h.onClickBad)中会报错,因为uiElement.addClickListener接受回调函数作为参数,该回调函数中的this的定义是void,而h.onClickBad这个函数真实的this定义是Handler。

void != Handler

因此会报错。

这种情况,我们只需要修改addClickListener接受回调参数的this定义即可:

interface UIElement {
  addClickListener(onclick: (this: Handle, e: Event) => void): void;
}

当然修改h.onClickBad中的this定义也能达到相同的效果。

二、Typescript中箭头函数和this声明

    抛开this声明,仅仅考虑this的话,typescript中箭头函数的this和ES6中箭头函数中的this是一致的。

箭头函数中的this,指向定义该函数时的那个对象。

    这里顺便补充一下什么是箭头函数中定义时绑定this对象。我们来举两个例子:

(1)、

var x=11;
var obj={
  x:22,
  say:function(){
    console.log(this.x)
  }
}
obj.say();
//console.log输出的是22

var x=11;
var obj={
 x:22,
 say:()=>{
   console.log(this.x);
 }
}
obj.say();
//输出11

    这是最简单的说明箭头函数中this的例子,箭头函数中的this,指向定义该函数时的执行上下文,在obj中定义的say方法,在定义的say方法的时候,其所在执行上下文为window,因此在箭头函数的例子中输出this.x === window.x,因此是11.

(2)、再来看一个复杂的例子:

var a=11
function test1(){
  this.a=22;
  let b=function(){
    console.log(this.a);
  };
  b();
}
var x=new test1();
//输出11

var a=11;
function test2(){
  this.a=22;
  let b=()=>{console.log(this.a)}
  b();
}
var x=new test2();
//输出 22


上面的例子,不一一说明。总之只要记住在箭头函数中this,在定义的时候绑定。

    typescript规定,箭头函数不需要声明this的类型。

function test(this: { a: number }) {
  const b = () => {
    console.log(this);
  };
  b();
}
test.call({ a: 6 }); // 输出this为{a:6}

function test(this: { a: number }) {
  const b = (this:{a:number}) => {
    console.log(this);
  };
  b();
}
test.call({ a: 6 }); // ts error An arrow function cannot have a 'this' parameter

    因此如果我们不想对于每一个带this的函数,都一一去声明this的类型,那么用箭头函数来代替普通函数,就可以省去声明this类型的过程。

三、Typescript与this相关的内置函数

(1)、ThisParameterType

     ThisParameter接受一个Type作为范型函数,这个Type必须是函数的类型,最后返回的是该函数中this的类型。这句话听起来很拗口,接着来看例子:

function test(this: number) {}
const n: ThisParameterType<typeof test> = 1;
//不会报错 ThisParameterType<typeof test> 最后的类型就是number

上述中typeof test就是一个函数的类型,然后因为test函数中this声明的类型为number,因此通过

ThisParameterType<typeof test>

拿到就是test函数中this声明的这个number类型。

(2)、OmitThisParameter

    OmitThisParameter是移除type类型中this声明的类型 。

function test(this: number) {}
const t: OmitThisParameter<typeof test> = test.bind(1);
t();

上述

OmitThisParameter<typeof test>

类型就是:()=> void

(3)、ThisType

     ThisType较为晦涩,ThisType一般用于字面量对象中,用于定义字面量对象的this。 比如:

interface Point {
    x: number;
    y: number;
   }
const myCustomObj: ThisType<Point> = {
    moveRight() {
        this.x++;
    },
    moveLeft() {
        this.x++;
    }
}

这里myCustomObj这个对象字面量类型中的this为Point,因此在myCustomObj这个对象内部使用this.x是类型安全的。

    此外thisType通常用于对象的组合和扩展中,来看一个更复杂的例子:

type BasicPoint = {
    x: number;
    y: number;
}
function makeCustomPoint(methods: ThisType<BasicPoint>) {
    return { x: 0, y: 0, ...methods };
}
const customPoint = makeCustomPoint({
    moveRightBy: function (xTranslate: number) {
        // "this.x" is a number 
        this.x += xTranslate;
    }
});