如何在传递函数时在编译时强制正确使用 “this”?

huangapple go评论61阅读模式
英文:

How to enforce correct usage of "this" at compile time when passing functions?

问题

当将方法传递给其他类时,很容易出现this上下文混乱的情况。让我们考虑以下简单示例:

class Counter {
    public count: number
    private callMyCallback: CallMyCallback
    constructor() {
        this.count = 0

        // works 
        this.callMyCallback = new CallMyCallback(() => this.countFunctionCalls())
        // doesn't work (Cannot read properties of undefined (reading 'count'))
        this.callMyCallback = new CallMyCallback(this.countFunctionCalls)
    }
    private countFunctionCalls() {
        console.log("Callback called")
        this.count = this.count + 1;
    }
}

class CallMyCallback {
    constructor(callback: () => void) {
        callback();
    }
}

const counter = new Counter()
console.log(counter.count)

我的问题是,将回调函数以这两种方式传递给callMyCallback都能够编译通过,没有出现错误或警告。只有在运行应用程序时才能检测到错误地使用了this。我没有找到任何对应的tsconfig设置或其他有用的内容。是否有任何设置/库/... 可以强制执行this的正确用法?

英文:

When passing methods to other classes it can easily happen that the context of this is messed up. Lets consider the following simple example:

class Counter {
    public count: number
    private callMyCallback: CallMyCallback
    constructor() {
        this.count = 0

        // works 
        this.callMyCallback = new CallMyCallback(() => this.countFunctionCalls())
        // doesn't work (Cannot read properties of undefined (reading 'count'))
        this.callMyCallback = new CallMyCallback(this.countFunctionCalls)
    }
    private countFunctionCalls() {
        console.log("Callback called")
        this.count = this.count + 1;
    }
}

class CallMyCallback {
    constructor(callback: ()=> void) {
        callback();
    }
}

const counter = new Counter()
console.log(counter.count)

My problem is that both ways of passing the callback function to callMyCallback are compiled without error or warning. Using this wrongly can only be detected when running the application. I didn't find any corresponding tsconfig setting, or anything else helpful. Is there any setting/library/... to enforce the correct usage of this?

答案1

得分: 1

抱歉,以下是翻译好的内容:

很不幸,TypeScript 不支持对 this 上下文的严格强制执行。已经有一个长期的开放请求,位于 microsoft/TypeScript#7968,但尚未实施(似乎很难做到并保持足够的编译器性能)。

您可以使用适当的 this 参数 注释单独的方法和函数,以获得对这些方法和函数的所需检查,但这不会自动发生:

class CallMyCallback {
  constructor(callback: (this: void) => void) {
    // 添加 -------------> ^^^^^^^^^^
    callback();
  }
}

class Counter {
  private countFunctionCalls(this: Counter) {
    // 添加 -----------------> ^^^^^^^^^^^^^
    console.log("Callback called")
    this.count = this.count + 1;
  }

  public count: number
  private callMyCallback: CallMyCallback

  constructor() {
    this.count = 0

    this.callMyCallback = new CallMyCallback(() => this.countFunctionCalls()) // 正确
    this.callMyCallback = new CallMyCallback(this.countFunctionCalls) // 错误!
    // -------------------------------------> ~~~~~~~~~~~~~~~~~~~~~~~
    //   每个签名的 'this' 类型不兼容。
  }
}

据我所知,目前这是您可以得到的最接近的方法。显然,这并不理想,因为开发人员需要记住添加这些检查,而且一个知道自己需要这样做的开发人员可能会避免首先传递未绑定的方法。

因此,如果您希望看到这种情况发生变化,去 microsoft/TypeScript#7968 一下,给它点个👍,并且(因为该问题被标记为“等待更多反馈”)提供一条评论,解释您的用例、为什么它引人注目,以及为什么现有的解决方法不够。这可能不会太有帮助(有许多未解决的功能请求,不能都实现),但也不会有害。

Playground 链接到代码

英文:

Unfortunately TypeScript doesn't support strict enforcement of this context. There is a longstanding open request for it at microsoft/TypeScript#7968 but it has not been implemented (it seems to be hard to do so and maintain adequate compiler performance).

You can annotate individual methods and functions with appropriate this parameters to get the desired checking on those methods and functions, but it doesn't happen automatically:

class CallMyCallback {
  constructor(callback: (this: void) => void) {
    // add ------------> ^^^^^^^^^^
    callback();
  }
}

class Counter {
  private countFunctionCalls(this: Counter) {
    // add ----------------> ^^^^^^^^^^^^^
    console.log("Callback called")
    this.count = this.count + 1;
  }

  public count: number
  private callMyCallback: CallMyCallback

  constructor() {
    this.count = 0

    this.callMyCallback = new CallMyCallback(() => this.countFunctionCalls()) // okay
    this.callMyCallback = new CallMyCallback(this.countFunctionCalls) // error!
    // ------------------------------------> ~~~~~~~~~~~~~~~~~~~~~~~
    //   The 'this' types of each signature are incompatible.
  }
}

This is currently the closest you can get, as far as I know. Obviously it's not great, because the developer needs to remember to add these checks, and a developer who's aware that they need to do this would probably avoid passing around unbound methods in the first place.

So, if you want to see this changed, it couldn't hurt to go to microsoft/TypeScript#7968, give it a 👍, and (because the issue is tagged as "Awaiting More Feedback") provide a comment on your use case, why it's compelling, and why the available workarounds don't suffice. It probably wouldn't help much (there are a lot of outstanding feature requests and they can't all be implemented), but it couldn't hurt.

Playground link to code

huangapple
  • 本文由 发表于 2023年6月22日 18:29:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76530957.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定