javascript - 如何指示从对象调用的绑定(bind)成员函数的上下文类型?

标签 javascript typescript this

我有以下(人为的)TypeScript 代码:

interface hasSpecialField {
  specialField: string;
}

interface hasStringFn {
  fn: (this: hasSpecialField) => string;
}

function whatIsMyContext(this: hasSpecialField): string {
  return this.specialField;
}

function getObj(): hasStringFn {
  let toBeBound: hasSpecialField = {
    specialField: 'Can whatIsMyContext() access me?'
  };

  let obj: hasStringFn = {
    fn: whatIsMyContext.bind(toBeBound)
  };

  return obj;
}

let parentObj = getObj();
let result = parentObj.fn();

console.log(result);

如果我写出并执行这段代码的 JavaScript 等价物,没有任何类型注释,我会看到以下内容:

Can whatIsMyContext() access me?
undefined

这是有道理的,因为 whatIsMyContext 明确绑定(bind)到 toBeBound,它具有 this.specialField 引用的字段。此外,最后一行是 console.log,它不返回值,因此 undefined

但是,TypeScript 会为此代码抛出错误:

The 'this' context of type 'hasStringFn' is not assignable to method's 'this' of type 'hasSpecialField'. Property 'specialField' is missing in type 'hasStringFn'.

错误消息指向 let result = parentObj.fn(); 行。似乎 TypeScript 认为 whatIsMyContext(别名为 fn)在从 parentObj 调用时有错误的 this 上下文。为什么会这样想?我应该如何编写代码来保留类型信息,同时向 TypeScript 发出上下文绑定(bind)正确的信号?

最佳答案

就类型检查而言,您声明的接口(interface) hasStringFn 本身是不可用的。您的声明说 fn 只能在具有 specialField 的对象上调用:

let a: hasStringFn;
let b: hasSpecialField;
a.fn();        // error
a.fn.call(b)   // ok

解决这个问题的一种可能方法是声明一个带有函数属性 fn 的接口(interface),它甚至没有 this 参数,就像独立函数一样:

interface hasBoundStringFn {
    fn: () => string;
}

然后这段代码编译:

function getObj(): hasBoundStringFn {
  let toBeBound: hasSpecialField = {
    specialField: 'Can whatIsMyContext() access me?'
  };

  let obj: hasStringFn = {
    fn: whatIsMyContext.bind(toBeBound)
  };

  return obj;
}

let parentObj = getObj();
let result = parentObj.fn();

下一个问题:

So am I correct in concluding that TypeScript is looking at the interface, not the implementation, when it throws that error? In other words, the code is valid but TypeScript looks no further than the interface in parsing it?

是的,如果你显式地声明变量或函数类型,编译器会相信你(只要它不与代码中的用法相矛盾)并且不会再看下去。

您可以通过尽可能多地删除类型注释来检查编译器将从实现中获得什么:

interface hasSpecialField {
  specialField: string;
}

function whatIsMyContext(this: hasSpecialField): string {
  return this.specialField;
}

function getObj() {
  let toBeBound = {
    specialField: 'Can whatIsMyContext() access me?'
  };

  let obj = {
    fn: whatIsMyContext.bind(toBeBound)
  };

  return obj;
}

let parentObj = getObj();
let result = parentObj.fn();

console.log(result);

在您的情况下,代码可以编译,但这并不是因为 TypeScript 确定实现是有效的。这是因为 getObj 的推断类型是

function getObj(): { fn: any; }

你看,bind 是内置的,编译器无法访问它的实现,它必须相信 bind 的类型声明,因为它在 TypeScript 标准中给出图书馆。

现在,bindcallapplyeffectively untyped .

因此在这种特殊情况下,您必须提供自己的显式类型和接口(interface),以使编译器检查您的代码。

关于javascript - 如何指示从对象调用的绑定(bind)成员函数的上下文类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44476972/

相关文章:

javascript - 如何将以下一系列 for 循环缩小为不太紧凑的代码?

typescript - 如何使用 redux-thunk 和 TypeScript 调度 ThunkAction

javascript - 在 JavaScript 原型(prototype)函数中保留对 "this"的引用

javascript - 是否可以将 browserify-css 与 Factor-bundle 一起使用?

javascript - 追加 child 不在 JavaScript 中工作

regex - 正则表达式逻辑

javascript - 如何使用 'this' 关键字规避 ES6 类范围问题

Javascript: "Top-level ' 这个“表达式”

JavaScript 隐藏/显示

angular - 用于根据环境变量更新 config.json 的预构建脚本