如何在 Vue2 中强制转换去抖动计算 setter?
How do I typecast a debounced computed setter in Vue2?
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>
这被 parent 用作 <my-input :value.sync="someProp" />
。它是一个更复杂的表单系统的一部分,具有不同类型的输入,配置驱动。但这与我提出的问题无关。当我不去抖动时,一切都按预期工作。如中,所有内容均已正确输入和推断。
我的问题是我不知道如何告诉 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
,但我仍然不知道用什么来替换它。
在匿名函数中使用 this
总是很棘手,因为 this
的值取决于函数的调用方式。 Lodash 可能在这里做了正确的事情,明确地照顾 this
但 TS 不知道。
最简单的解决方法通常是像这样在局部变量中“捕获”this
的值:
methods: {
someMethod() {
const self = this
setTimeout(function() { self.doSomething() }, 500)
}
}
您的代码当然不可能做到这一点 但是 我认为您的代码还有一个问题,那就是您创建去抖动包装器的方式。它在 Vue 3 文档中是 hinted,但也适用于 Vue 2
this approach is potentially problematic for components that are reused because they'll all share the same debounced function.
解决这个问题(同时解决 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)
}
Edit - caveat:
It is worth noting that, as Michal Levý has mentioned in the other answer, using debounced
in a computed setter directly is definitely not recommended for the reasons mentioned in his answer - the debounced function should exist per instance, or some weird behaviour is bound to emerge. That being said, this answer still stands as a way to add a nicer this
to functions-as-arguments in the configuration object
TLDR:简短的回答是你不能,除非你付出太多努力才值得。但是您可以将 this
键入 Vue
,这是下一个最好的选择,并且对 99% 的情况都有用。
问题在于 Typescript 和类型推断:Vue 打字使用 ThisType to inject context into the various methods defined in the configuration objects (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 中有特色
我们用作游乐场的沙箱组件。
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>
这被 parent 用作 <my-input :value.sync="someProp" />
。它是一个更复杂的表单系统的一部分,具有不同类型的输入,配置驱动。但这与我提出的问题无关。当我不去抖动时,一切都按预期工作。如中,所有内容均已正确输入和推断。
我的问题是我不知道如何告诉 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
,但我仍然不知道用什么来替换它。
在匿名函数中使用 this
总是很棘手,因为 this
的值取决于函数的调用方式。 Lodash 可能在这里做了正确的事情,明确地照顾 this
但 TS 不知道。
最简单的解决方法通常是像这样在局部变量中“捕获”this
的值:
methods: {
someMethod() {
const self = this
setTimeout(function() { self.doSomething() }, 500)
}
}
您的代码当然不可能做到这一点 但是 我认为您的代码还有一个问题,那就是您创建去抖动包装器的方式。它在 Vue 3 文档中是 hinted,但也适用于 Vue 2
this approach is potentially problematic for components that are reused because they'll all share the same debounced function.
解决这个问题(同时解决 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)
}
Edit - caveat:
It is worth noting that, as Michal Levý has mentioned in the other answer, using
debounced
in a computed setter directly is definitely not recommended for the reasons mentioned in his answer - the debounced function should exist per instance, or some weird behaviour is bound to emerge. That being said, this answer still stands as a way to add a nicerthis
to functions-as-arguments in the configuration object
TLDR:简短的回答是你不能,除非你付出太多努力才值得。但是您可以将 this
键入 Vue
,这是下一个最好的选择,并且对 99% 的情况都有用。
问题在于 Typescript 和类型推断:Vue 打字使用 ThisType to inject context into the various methods defined in the configuration objects (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 中有特色 我们用作游乐场的沙箱组件。