当我知道`this`是动态的时,可以使用Typescript将经典函数作为参数来代替箭头函数吗?

时间:2019-11-01 10:24:32

标签: typescript

某些API希望接收必须与this进行交互的函数,因此箭头函数无法在其中工作,例如一个knex子查询。

是否可以向Typescript暗示期望具有未绑定this的函数?

1 个答案:

答案 0 :(得分:2)

长话短说:不幸的是,没有很多额外的手动输入类方法的方法,就无法执行此操作。


TypeScript具有this parameters,其中函数的调用签名具有名为this的初始函数参数,该参数指定对调用函数时所需的this上下文类型的约束。 this参数是“ fake”,因为在调用函数时实际上并没有将其作为第一个参数传递。例如:

function sayMyName(this: { name: string }) {
  console.log("Hi, my name is " + this.name + ".");
}

sayMyName()函数不带任何参数,但是只有绑定到类型为{name: string}的对象时才能调用它。这会在编译时捕获以下错误:

try {
  sayMyName(); // error, won't work unless bound
} catch (e) {
  console.log(e); // TypeError: this is undefined
}

并允许这样的事情:

sayMyName.bind({ name: "Harry Potter" })(); // okay
// Hi, my name is Harry Potter.

const hermione = {
  name: "Hermione Granger",
  speak: sayMyName
}
hermione.speak(); // okay
// Hi, my name is Hermione Granger.

因此,解决此问题的显而易见的尝试是让您传入的函数指定其this上下文应为void,如下所示:

function callbackCaller(cb: (this: void) => void): void {
  cb(); // okay
}

这确实可以通过箭头/未绑定函数以及sayMyName来表现您想要的方式:

const arrow = () => console.log("I am an arrow function");
callbackCaller(arrow); // okay
// I am an arrow function

const anonymous = function () { console.log("I am an anonymous function"); }
callbackCaller(anonymous); // okay
// I am an anonymous function

try {
  callbackCaller(sayMyName); // error, 'this' types are incompatible
} catch (e) {
  console.log(e); // TypeError: this is undefined
}

但是,当您使用类方法尝试时会遇到问题:

class Weasley {
  constructor(private givenName: string) { }
  greet() {
    console.log("Hi, my name is " + this.givenName + " Weasley.");
  }
}

const ron = new Weasley("Ron");
ron.greet(); // Hi, my name is Ron Weasley.

try {
  callbackCaller(ron.greet); // oops, no compile error!!!
} catch (e) {
  console.log(e); // TypeError: this is undefined
}

TypeScript完全无法捕获ron.greet具有this类型的Weasley上下文,而是将其视为this上下文属于{{1}的类型}。最初,在实现any参数时,将有一个this编译器标志来处理此问题,但该标志已被删除。建议添加open issue in Github,但目前此功能不是该语言的一部分。 (如果您希望看到它添加的内容,则可能要解决该问题,并给它一个?或描述它的用例,如果它比现有的方法更具吸引力。)

解决方法是采用您关心的任何类方法,并明确地为其指定--strictThis参数。可以使用的合理类型是polymorphic this type,其最终结果如下:

this

解决了这个问题:

class Malfoy {
  constructor(private givenName: string) { }
  greet(this: this) { // <-- this parameter of type this
    console.log("It is I, " + this.givenName + " Malfoy.");
  }
}

以要求人们使用const draco = new Malfoy("Draco"); draco.greet(); // It is I, Draco Malfoy. try { callbackCaller(draco.greet); // error, won't work unless bound } catch (e) { console.log(e); // TypeError: this is undefined } 修饰类方法的代价为代价。这对您来说可能是一个破坏交易的事情,因为您根本无法要求其他人这样做。我不知道。


可能可以使用一些奇特的类型处理来强制TypeScript向类的所有方法中添加this: this参数,例如:

this

但是同样,在别人的方法传递给您的type Thisify<T> = { [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (this: Thisify<T>, ...args: A) => R : T[K] } const thisify = <T>(instance: T) => instance as Thisify<T>; const reformedRon = thisify(ron); reformedRon.greet(); // Hi, my name is Ron Weasley. try { callbackCaller(reformedRon.greet); // error, won't work unless bound } catch (e) { console.log(e); // TypeError: this is undefined } 函数之前,您可能没有机会{@ {1}}。我认为,如果没有thisify(),我们就会陷入困境。


好的,希望仍然有帮助。祝你好运!

Link to code