精进TypeScript--提供回调中this的类型

JavaScript 的 this 关键字是整个语言中最令人困惑的部分之一。与 let 和 const 声明的变量是具有词法作用域的,与之不同的是,this 是动态作用域:它的值不取决于它怎么定义,而取决于它怎么调用

要记住的事情:

  • 了解 this 绑定的工作原理
  • 当 this 成为你的 API 的一部分时,应该在回调中为它提供一个类型。

this最常用于类中,通常指向对象的当前实例:

class C {
  vals = [1, 2, 3];
  logSquares() {
    for (const val of this.vals) {
      console.log(val * val);
    }
  }
}

const c = new C();
c.logSqures(); // 输出 1 4 9

如果你把 logSquares 放在一个变量中并调用它会发生什么:

const c = new C();
const method = c.logSqures();
method(); // ~~ uncaught TypeError: Cannot read property 'vals' of undefined

问题在于,c.logSqures() 实际上做了两件事:调用 C.prototype.logSqures,并且将该函数中 this 的值绑定到 c 上。通过引出一个对 logSquares 的引用,你把两件事分开了,this 被设置为 undefined。

JavaScript 允许你完全控制this的绑定。你可以使用 call 来显式地设置 this 并解决这个问题:

const c = new C();
const method = c.logSqures();
method.call(c);

没有理由让 this 必须绑定到 C 的实例,它可以被绑定到任何东西上。所以,库可以将 this 的值作为其 API 的一部分,甚至 DOM 也利用了这一点。例如,在一个事件处理程序中:

document.querySelector('input').addEventListener('change', function(e) {
  console.log(this); // 记录事件触发时的输入元素
}

this 绑定经常出现在这样的回调中。例如,如果你想在一个类中定义一个 onClick 处理程序,你可以这样:

class ResetButton {
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
  onclick() {
    alert(`Reset ${this}`);
  }
}

当 Button 调用 onClick 时,它会弹出 “Reset undefined”。同样的,问题出在 this 绑定上。一个常见的解决方案是在构造函数中创建方法的绑定:

class ResetButton {
  constructor() {
    this.onClick = this.onClick.bind(this);
  }
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
  onclick() {
    alert(`Reset ${this}`);
  }
}

onClick() {…} 在 ResetButton.prototype 上定义了一个属性,这个属性被所以的 ResetButton实例 共享。当你在构造函数中绑定 this.onClick = … 时,它会在 ResetButton 的实例上创建一个名为onClick 的属性,并将 this 绑定到该实例上。在查找序列中,onClick 实例属性排在 onClick 原型属性之前,所以 this.onClick 指的是 render() 方法中已绑定 this 的函数。

绑定有一个快捷方法,使用箭头函数:

class ResetButton {
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
  onclick = () => {
    alert(`Reset ${this}`); // “this”永远指向 ResetButton 实例
  }
}

看一下生成的 JavaScript 理解一下:

class ResetButton {
  constructor() {
    var _this = this;
    this.onClick = function() {
      alert("Reset " + _this);
    };
  }
  render() {
    return makeButton({text: 'Reset', onClick: this.onClick});
  }
}

那么这一切和 TypeScript 有什么关系呢?因为 this 绑定是 JavaScript 的一部分,TypeScript 需要对它进行建模。

你可以添加一个this参数到你的回调中:

function addKeyListener(
  el: HTMLElement,
  fn: (this: HTMLElement, e: KeyboardEvent) => void
) {
  el.addEventListener('keydown', e => {
    fn(el, e); // ~ 期待输入1个参数,但得到2个
  });
}

更好的是, TypeScript 会强制要求你用正确的 this 上下文来调用函数:

function addKeyListener(
  el: HTMLElement,
  fn: (this: HTMLElement, e: KeyboardEvent) => void
) {
  el.addEventListener('keydown', e => {
    fn(el); // ~~ 类型为“void” 上下文不可分配给类型为“HTMLElement” 的方法“this”
  });
}

作为这个函数的使用者,你可以在回调中引用 this,并获得完全的类型安全:

declare let el: HTMLElement;
addKeyListener(el, function(e) {
  this.innerHTML; // OK,“this”的类型是 HTMLElement
});

当然,如果你在这里使用一个箭头函数,this 就会被覆盖。TypeScript 会捕获这个问题:

class Foo {
  registerHandler(el: HTMLElement) {
    addKeyListener(el, e => {
      this.innerHTML; // ~~ 类型“Foo”上不存在属性“innerHTML”
    });
  }
}

不要忘记 this! 如果你在回调中设置 this 的值,那么它就是你的 API 的一部分,就应该在你的类型声明中包含它。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
vue-typescript-import-dts 是一个用于为 Vue.js 项目TypeScript 文件生成类型声明文件的工具。在 Vue.js 项目使用 TypeScript 进行开发时,我们经常需要为一些第三方库或自定义组件编写类型声明文件,以提供更好的代码提示和类型检查。 使用 vue-typescript-import-dts 工具可以自动分析 TypeScript 文件的导入语句,并根据导入的模块生成对应的类型声明文件。这样,在使用该模块时,IDE 或编辑器就能提供准确的代码补全和类型检查。 例如,假设我们的项目使用了一个名为 axios 的第三方库进行网络请求,但是该库并没有提供类型声明文件。我们可以通过 vue-typescript-import-dts 工具,在我们的 TypeScript 文件导入 axios,并正确配置工具,它将自动为我们生成一个 axios.d.ts 类型声明文件。 具体使用 vue-typescript-import-dts 的步骤如下: 1. 在项目安装 vue-typescript-import-dts,可以使用 npm 或 yarn 命令来安装。 2. 在 TypeScript 文件,使用 import 语句导入需要生成类型声明文件的模块。 3. 在项目根目录下创建一个 .vue-typescript-import-dts.json 配置文件,用来配置生成类型声明文件的规则。可以指定生成的声明文件的输出路径、文件名等。 4. 运行 vue-typescript-import-dts 命令,它会自动扫描 TypeScript 文件的导入语句,并根据配置生成相应的类型声明文件。 这样,在我们编写代码时,IDE 或编辑器就可以准确地为我们提供代码补全和类型检查的功能。这对于提高开发效率和代码质量非常有帮助。 总之,vue-typescript-import-dts 是一个便捷的工具,可以自动为 Vue.js 项目使用的第三方库或自定义组件生成类型声明文件,提供更好的代码提示和类型检查功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~卷心菜~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值