Issue
Migrating VueJS project from JavaScript + Options API to TypeScript + Composition API, I have gradually found equivalents of most of the stuff. One thing that I'm struggling with is the v-model
feature. I found a good article on implementing it using Composition API, where author creates a composable function that can be reused in components that want to implement v-model
. I'm trying to now write an equivalent function using TypeScript.
Here is the original JS code:
import { computed } from 'vue'
export function useModelWrapper(props, emit, name = 'modelValue') {
return computed({
get: () => props[name],
set: (value) => emit(`update:${name}`, value)
})
}
My TS implementation looks like this:
import { computed, ComputedRef } from '@vue/composition-api'
export function useModelWrapper<T> (props: Record<string, unknown>, emit: (event: string, value: T) => void, name: 'modelValue') : ComputedRef {
return computed({
get: () => props[name],
set: (value) => emit(`update:${name}`, <T>value)
})
}
This compiles fine but Vue is not too happy about it. I get
No overload matches this call
error if I use this function in a component.
My suspicion is that the type of props
in the TS version is not correct, but I was not able to figure out what type I should use there. I have tried it with Object
, unknown
and any
, but none of them allows me to do props[name]
in the getter (any
does but TS complains that I shouldn't use any
as the type of props
).
What am I doing wrong here?
Edit
Here is the full error text:
{
"resource": "../ProjectsDropdown.vue",
"owner": "_generated_diagnostic_collection_name_#6",
"code": "2769",
"severity": 8,
"message": "No overload matches this call.
Overload 1 of 3, '(options: ComponentOptionsWithoutProps<unknown, unknown, Data, {}, {}>): VueProxy<unknown, unknown, Data, {}, {}>', gave the following error.
Type '{ modelValue: PropType<Project>; }' is not assignable to type 'undefined'.
Overload 2 of 3, '(options: ComponentOptionsWithArrayProps<string, Data, Data, {}, {}, Readonly<{ [x: string]: any; }>>): VueProxy<Readonly<{ [x: string]: any; }>, Data, Data, {}, {}>', gave the following error.
Type '{ modelValue: PropType<Project>; }' is not assignable to type 'string[]'.
Object literal may only specify known properties, and 'modelValue' does not exist in type 'string[]'.
Overload 3 of 3, '(options: ComponentOptionsWithProps<ComponentPropsOptions<Data>, Data, Data, {}, {}, ({ [x: number]: string; } & { [iterator]?: IterableIterator<string> | undefined; ... 32 more ...;
toLocaleString?: string | undefined; }) | ({} & { ...; })>): VueProxy<...>', gave the following error.
Type '{ modelValue: PropType<Project>; }' is not assignable to type 'ComponentPropsOptions<Data> | undefined'.
Types of property 'modelValue' are incompatible.
Type 'PropType<Project>' is not assignable to type 'Prop<unknown, unknown> | null | undefined'.
Type 'new (...args: never[]) => Project & object' is not assignable to type 'Prop<unknown, unknown> | null | undefined'.
Type 'new (...args: never[]) => Project & object' is not assignable to type 'new (...args: string[]) => Function'.
Types of parameters 'args' and 'args' are incompatible.
Type 'string' is not assignable to type 'never'.",
"source": "Vetur",
"startLineNumber": 59,
"startColumn": 16,
"endLineNumber": 119,
"endColumn": 3
}
Edit 2
Here is how I'm using this function in a component:
<script lang='ts'>
export default defineComponent({
...
import { useModelWrapper } from '../usemodelWrapper'
props: {
modelValue: Object as PropType<Project>
},
setup (props, { emit }) {
return {
selectedProject: useModelWrapper<Project>(props, emit, 'modelValue')
}
})
</script>
Solution
After seeing your own answer, I feel like this could be even stricter.
import { computed, WritableComputedRef } from '@vue/composition-api'
export function useModelWrapper<TProps, TKey extends keyof TProps> (
props: TProps,
emit: (event: string, value: TProps[TKey]) => void,
name: TKey = 'modelValue' as TKey
) : WritableComputedRef<TProps[TKey]> {
return computed<TProps[TKey]>({
get: () => props[name],
set: (value: TProps[TKey]) => emit('input', value)
})
}
I haven't tested it with Vue specifically but that function should restrict which key you can pass it as third argument and correctly infer the return type depending on which name
was passed instead of a union of all values of props
.
Answered By - emeraldsanto
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.