When using TypeScript and vue-cli the following classic example does not compile with TypeScript 3.6.3:
export function useCounter() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2),
})
return state
}
because 'state' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.. I can "fix" it with following code but I wonder if there are better ways without resorting to manually creating a type definition for the state:
export function useCounter() {
let self: any = null
const state = reactive({
count: 0,
double: computed(() => self.count * 2),
})
self = state
return state
}
After all that is a common case and it does not work as expected...
I'm encountering the same problem, currently I do something like
interface Counter {
count: number,
double: number
}
export function useCounter() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
}) as Counter;
return state
}
Yeah, the advantage of that approach is that we can use Counter interface elsewhere when passing state to another function. The problem is obvious - we need to maintain code in 2 places.
Supporting for classes would be neat e.g.:
class Counter {
count = 0
double = computed(() => this.count * 2)
increment() {
this.count++
}
}
export function useCounter() {
const state = reactive(new Counter())
return state
}
what do you mean you need to maintain code in 2 places? defining types / interfaces describing shape of your data seems completely acceptable.
There seems to be no clean way to fix, as TS really can't handle the type of something self referring.
Yeah, too bad you can't fix TypeScript for me...
I think it's better to use ref in this case:
export function useCounter() {
const count = ref(0)
const double = computed(() => count.value * 2)
return {
count,
double
}
}
Good news, TS 3.7 will support Recursive Type References.
Good news indeed, but AFAIK following still not possible without classes / separate type:
export function useCounter() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
});
useSomeHook(state)
return state
}
function useSomeHook(state: any) {} // <- what type to use here?
That is less common so probably is not a big annoyance and ReturnType<useCounter> can be used as a workaround (did not try - I use any for now), but using classes seems to be much more natural in TypeScript and it is not clear why not to support them...
@vmihailenco What do you mean that "class is not supported"?
I mean that following does not work for me:
class Counter {
count = 0
double = computed(() => this.count * 2)
increment() {
this.count++
}
}
export function useCounter() {
const state = reactive(new Counter())
return state
}
function useSomeHook(state: Counter) {} // <- solved
When calling increment this seems to be null/undefined. I did not try, but computed is likely broken too. I tried naive fix like state.increment.bind(state) but it did not work out.
Most helpful comment
I'm encountering the same problem, currently I do something like