Add type inference to Options API provide / inject or add docs for not supporting it
Version
3.0.5
Reproduction link
https://github.com/l0rn/inject-provide-types/tree/options-api
Steps to reproduce
Use the Provide/Inject mechanism using the Options API and Typescript.
Injected properties won't be inferred and will throw type errors complaining the property is not defined on the component. On the main branch of the same repository you will find the same being achieved using composition API, which works fine.
As far as I understand only using Typescript should not be a reason to use the Composition API I think it would be a cool effort trying to add typing support to inject using the Options API. If that's not feasible I think a warning in the documentation would be appropriate.
What is expected?
Best case scenario:
- Use provide / inject in options API, all types are properly inferred
- If any additional type declaration is needed, a paragraph on inject / provide in the typescript section of the docs is provided
What is actually happening?
Using provide / inject in my typescript code, wondering why the injected property is not available in typescript:
ERROR in src/components/Marker.vue:23:19
TS2339: Property 'map' does not exist on type 'ComponentPublicInstance<Readonly<{ location: LocationDto; } & {}>, {}, {}, {}, {}, Record<string, any>, Readonly<{ location: LocationDto; } & {}>, {}, false, ComponentOptionsBase<...>>'.
21 | marker
22 | .setLngLat([this.location.lng, this.location.lat])
> 23 | .addTo(this.map.value)
| ^^^
24 |
25 | }
26 | })
Would have been helpful to keep the repro online, but it seems you delete the branch demonstrating your issue. :/
Hm, i just double checked. I clicked the link in a non-logged in private window and it still shows correctly :thinking:
Maybe the permalink to the problematic line helps? https://github.com/l0rn/inject-provide-types/blob/75743eb91230424db07f6dbfb4095a918c890da7/src/components/Marker.vue#L23
I think this is a request to get the type when using inject
with Options API
defineComponent({
inject: [ 'myVar'],
mounted(){
this.myVar // doesn't exists
}
})
Ideally we would also be able to get the typing information, it would be cool to use the InjectionKey<T>
for that
const myVar: InjectionKey<{a: 1}> = Symbol()
defineComponent({
inject: [ myVar],
mounted(){
this.myVar // it should have type of `{a: 1}`
}
})
Yes so basically I ran into what I believe is the issue that l0rn is trying to raise here.
Given a vue-cli generated Vue 3 project, the typescript compiler will complain about the injected value not being present on the component when using TypeScript in combination with the Options API. Now, I might be missing something here but I have yet to figure out a way to tackle this.
Yes so basically I ran into what I believe is the issue that l0rn is trying to raise here.
Given a vue-cli generated Vue 3 project, the typescript compiler will complain about the injected value not being present on the component when using TypeScript in combination with the Options API. Now, I might be missing something here but I have yet to figure out a way to tackle this.
I just ran into this as well. Here's a tweet from @sstephenson showing an example type definition for a similar api in StimulusJS. It doesn't look like he was able to keep the array syntax to make this work.
I'm going to convert my component to the Composition API and see if that fixes that issue.
Im not familar with inject
. If it only accepets string or symbol, best we can do is infering this.map
as a unkown
or any
.
Yeah this would be really useful, its pretty frustrating to not be able to use inject, options API, and TypeScript together
Okay, I ran into the same issue, and ended up using the inject method, inside a setup
function in my Options API component. It's goofy, but it does the trick:
<template>
<h1>{ a! + b! }<h1>
<h2>combined: { combined }</h2>
</template>
<script lang="ts">
import { defineComponent, inject } from 'vue'
import type { A, B } from './things'
export default defineComponent({
setup () {
return {
a: inject<A>('a'),
b: inject<B>('b')
}
},
computed: {
combined () : string {
// Typescript treats injects as optionals, so I'm explicitly expanding them with `!`
return this.a! + this.b!
}
}
})
</script>
@8bitDesigner that's the correct behaviour, for non optional you need to pass a default
.
The reason is depending where you are in the tree it might be or not provided by a parent. Even though "you know" that will never happen, it wouldn't be the first time during refactoring or someone else changing the code that might place that component where it shouldn't be.