如何在 Vue2 中对去抖动的计算设置器进行类型转换?

Posted

技术标签:

【中文标题】如何在 Vue2 中对去抖动的计算设置器进行类型转换?【英文标题】:How do I typecast a debounced computed setter in Vue2? 【发布时间】:2021-07-13 12:31:55 【问题描述】:

Vue:2.6.11 打字稿:3.9.3 代码沙盒:here.

这是我正在尝试做的简化版本:

<template>
  <input v-model="computedValue" type="number">
</template>
<script lang="ts">
  import Vue from 'vue';
  import  debounce  from 'lodash-es';

  export default Vue.extend(
    name: 'MyInput',
    props: 
      value: 
        type: Number,
        required: true
      
    ,
    computed: 
      computedValue: 
        get(): number 
          return this.value;
        ,
        set: debounce(function(value) 
          this.$emit('update:value', value);
        , 500)
      
    
  );
</script>

这被父级用作&lt;my-input :value.sync="someProp" /&gt;。它是更复杂的表单系统的一部分,具有不同类型的输入,配置驱动。但这与我提出的问题无关。当我不去抖动时,一切都按预期工作。例如,所有内容都已正确输入和推断。

我的问题是我不知道如何告诉 Typescript 将外部 this 投射到去抖函数的 this 上,这是 debounce 在引擎盖下所做的。

实际的打字稿错误是:

TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

代码有效(计算的 setter 已去抖动),但 Typescript 抱怨不知道去抖动函数中的 this 是什么。 如果我将 debounced 函数从普通函数更改为箭头函数,Typescript 不再对我咆哮(它现在知道this 是什么),但代码停止工作。

有谁知道如何在 debounced 函数中正确地转换 this 的值?

注意:不建议将/* @ts-ignore */ 放在上面一行。这就是我目前正在做的事情。

另一个注意事项:我的猜测是我必须重写 debounce 的定义类型,它目前来自 @types/lodash-es,但我仍然不知道用什么替换它。

【问题讨论】:

我不熟悉 Vue 中的 typescript 语法;但是我确实像您一样使用 debounce 功能(使用function()...),它在常规 Vue 中完美运行。我个人不会被 ts-ignore 这条线打扰 当我的 setter 出现 3 次或更多 this 时,我开始对此感到困扰。现在我需要3 × /* @ts-ignore */ 哦,显然这是打字稿中的一个未解决问题,您不能忽略代码块 atm,仅每行 【参考方案1】:

编辑 - 警告:

值得注意的是,正如 Michal Levý 在另一个答案中提到的那样,绝对不推荐直接在计算的 setter 中使用 debounced,因为他的答案中提到了原因 - debounced 函数每个实例都应该存在,否则一定会出现一些奇怪的行为。话虽这么说,这个答案仍然是一种将更好的this 添加到配置对象中的函数作为参数的方式

TLDR: 简短的回答是你不能,除非为了它值得付出太多的努力。但是您可以将this 输入为Vue,这对 99% 的情况来说是次优且有用的。

问题在于 Typescript 和 Type inference:Vue 类型使用 ThisType 将上下文注入到配置对象 (see the source) 中定义的各种方法中,这对于就地定义的函数很好,但使用 debounce意味着你传递的函数不是这个过程的一部分——它只是 debounce 本身的一个参数,所以 TS 不知道向它注入任何东西。

我知道的最简单的解决方案是将Vue 注入回函数中。有关更多信息,请参阅the docs,但要点是:在输入中传递this 参数作为上下文输入(因为this 无论如何都是保留字),所以这是有效的打字稿:

    computedValue: 
      get(): number 
        return this.value;
      ,
      set: debounce(function (this: Vue, value: number) 
        this.$emit('update:value', value);
      , 500),
    ,

优点:它为您提供所有 Vue 类型 坏处:除非您手动定义所有内容,否则没有实际上下文:

set: debounce(function (this: CombinedVueInstance<...>, value: number) 

但如果您需要这样做,直接在计算的 setter 中使用 debounce 将不再有用(更容易在 created 中设置去抖动函数,如另一个答案中所述)。


由 OP 编辑​​:我认为分享 Tiago 提出的更优雅的解决方案(对于有类似问题的未来用户)很重要(随着讨论的继续,在 SO 之外)。它在MyInput.vue 内有特色 我们用作游乐场的沙盒组件。

【讨论】:

【参考方案2】:

在匿名函数中使用this 总是很棘手,因为this 的值取决于函数的调用方式。 Lodash 在这里明确地处理 this 可能是正确的,但 TS 不知道这一点。

最简单的解决方法通常是在局部变量中“捕获”this 的值,如下所示:

methods: 
  someMethod() 
    const self = this
    setTimeout(function()  self.doSomething() , 500)
  

您的代码当然不可能做到这一点但是我认为您的代码还有一个问题,这就是您创建去抖动包装器的方式。它在 Vue 3 文档中是 hinted,但也适用于 Vue 2

这种方法对于重用的组件可能存在问题,因为它们都将共享相同的去抖动功能。

要解决这个问题(同时解决 TS 问题):

computed: 
      computedValue: 
        get(): number 
          return this.value;
        ,
        set(value): void 
          this.debouncedSetter(value)
        
      
    ,
created() 
  self = this
  this.debouncedSetter = debounce(function(value) 
    self.$emit('update:value', value);
  , 500)

【讨论】:

我同意您提出的解决方案可行,但我个人认为这是一种黑客行为。此外,它是 TypeScript 需要的 hack,而不是 javascript。在我看来,这与 Typescript 在开发中的作用相矛盾。至少在原则上,使用 Typescript 应该在 JavaScript 输出上有一个 null 足迹,而您的建议是将 JS 更改为取悦 TS。我发现在这种情况下使用/* ts-ignore */ 是一种概念上更正确的解决方案(除非有一种方法可以实际告诉 TypeScript debounce 做了什么,而无需创建debouncethis 的多个副本)。 但是您的代码在 JS 级别不正确。当然,除非您想在组件的所有实例之间共享 一个 去抖动包装器 在组件的多个实例之间共享去抖动包装器是一个不同的问题,而不是我要问的问题。并且它不会改变 Typescript 在 debounced 函数中无法按预期工作的事实。只有当我将该函数设为箭头函数时,它才能正常工作,而在 JavaScript 中,它会使其不再正确执行。请注意,我已经添加了一个包含该问题的代码框。

以上是关于如何在 Vue2 中对去抖动的计算设置器进行类型转换?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 中对装饰器工厂输入进行单元测试

如何在 Python 中对装饰器进行分组

如何在 C# 中对泛型类型指定多个约束?

如何在Vue中对具有2个字段的数组进行排序?

如何使用textureLod在glsl中对mip级别进行采样?

/*java初学者*/ java中对带小数的计算结果进行四舍五入、去尾法、进一法如何进行?