Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type inference to Options API provide / inject or add docs for not supporting it #3031

Closed
l0rn opened this issue Jan 16, 2021 · 10 comments · Fixed by #6804
Closed

Add type inference to Options API provide / inject or add docs for not supporting it #3031

l0rn opened this issue Jan 16, 2021 · 10 comments · Fixed by #6804

Comments

@l0rn
Copy link

l0rn commented Jan 16, 2021

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 | })

@LinusBorg
Copy link
Member

Would have been helpful to keep the repro online, but it seems you delete the branch demonstrating your issue. :/

@l0rn
Copy link
Author

l0rn commented Jan 16, 2021

Hm, i just double checked. I clicked the link in a non-logged in private window and it still shows correctly 🤔

Maybe the permalink to the problematic line helps?
https://github.com/l0rn/inject-provide-types/blob/75743eb91230424db07f6dbfb4095a918c890da7/src/components/Marker.vue#L23

@pikax
Copy link
Member

pikax commented Jan 16, 2021

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}`
 }
})

@99linesofcode
Copy link

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.

@JayDouglass
Copy link

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.

@hyf0
Copy link
Contributor

hyf0 commented May 25, 2021

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.

@dospunk
Copy link

dospunk commented Jul 30, 2021

Yeah this would be really useful, its pretty frustrating to not be able to use inject, options API, and TypeScript together

@8bitDesigner
Copy link

8bitDesigner commented Apr 1, 2022

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>

@pikax
Copy link
Member

pikax commented Apr 1, 2022

@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.

@8bitDesigner
Copy link

That totally makes sense, and I fully admit that I will break this the minute I refactor my code, if I haven't already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants