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

时间:2017-06-10 18:54:34

标签: 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显式绑定到toBeBoundthis.specialField具有console.log引用的字段。此外,最后一行是undefined,它不返回值,因此let result = parentObj.fn();

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

  

' this'类型的上下文有hasStringFn'不能分配方法'这个'类型' hasSpecialField'。物业' specialField'缺少类型' hasStringFn'。

错误消息指向行whatIsMyContext。从fn调用时,TypeScript似乎认为thisparentObj别名)具有错误的{{1}}上下文。为什么会这么想?我应该如何编写代码以保留类型信息,同时向TypeScript发出上下文绑定正确的信号?

1 个答案:

答案 0 :(得分:2)

就类型检查而言,接口hasStringFn正如您声明的那样,它本身无法使用。您的声明说fn只能在具有specialField的对象上调用:

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

一种可能的方法是声明一个带有函数属性fn的接口,它甚至没有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();

下一个问题:

  

所以我的结论是TypeScript正在查看   接口,而不是实现,当它抛出该错误?其他   单词,代码是有效的,但TypeScript看起来没有比   解析它的界面?

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

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

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是内置的,编译器无法访问它的实现,它必须相信TypeScript标准库中给出的bind的类型声明。

现在,bindcallapplyeffectively untyped

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