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>
propssection has a propertymodelValue: Object as PropType<Project>and the object returned bysetup()function has a memberselectedProject: useModelWrapper(props, emit, 'modelValue'), name: 'modelValue')part. This is a type declaration not a default value assignment. Try with itname = 'modelValue')