vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.82k stars 33.67k forks source link

'inject' Properties are not added to the CombinedVueInstance type definition #8969

Open StormBurpee opened 5 years ago

StormBurpee commented 5 years ago

Version

2.5.17

Reproduction link

https://codesandbox.io/s/rr18r3vm9p

Steps to reproduce

  1. Copy Minimal reproduction link into a local environment, and run the webpack compilation process.

OR

  1. Initialize a vue vm
    let vm = new Vue({
    el: "#app",
    render: (h: any) => h(someComponent, {}),
    provide: { service: { something: "Hello, World" } }
    });
  2. Try and access service in a SFC
export default Vue.extend({
    name: "someComponent",
    inject: ["service"],
    data() {
        return {
            accessService: this.service.something // Property 'service' does not exist on type CombinedVueInstance...
        }
    }
});

What is expected?

When declaring injections in a component in typescript, you should be able to access the injection with this.injection

What is actually happening?

When accessing an injection in a vue single file component, it is currently throwing an error during the webpack compilation process, stating that the injection Property 'injection' does not exist on type 'CombinedVueInstance<Vue...


Please note: that the link to minimal reproduction won't show the error logs from the webpack compiling, as it will compile successfully, but with errors. This will need to be tested in a local environment to see what is happening.

As this is in typescript, we're currently using Webpack to compile it to a single file, and then use this on our application.

The compilation will complete successfully, however will print multiple errors to the console after compiling, about not being able to access properties, etc. When running in the browser it works successfully.

We've dug around in the vue/types folder, and to the best of our knowledge think that Inject should be a part of the type DataDef or something of this sort.

Is there possibly a temporary workaround that we can use to avoid having these errors, until a fixed release is proposed?

XLearner commented 5 years ago

Yep, I come across a similar question. I have written the property "provide" in father vue-component and have used "inject" in son vue-component, but the chrome console show: [Vue warn]: Injection "color" not found. Here is my sectional code.

/** parent **/ ... export default { name: "father", provide: { color: 'blue' }, components: { son }, ... /** son **/ ... export default { name: 'pagesTable', inject: ['color'], mounted(){ console.log(this.color); } } ...

And then image

filimon-danopoulos-stratsys commented 4 years ago

Any news on this?

klinkby commented 4 years ago

Issue open >1 year. Does that mean DI is not supported with TypeScript?

wllmsash commented 4 years ago

I'm working around this by modifying Vue in my component. Example:

// main.ts

import Vue from 'vue';
import App from './App.vue';

export interface Collection {
  dog: string;
  cat: number;
  cow: boolean;
}

const myCollection: Collection = {
  dog: 'dog',
  cat: 0,
  cow: false,
};

export const myServices = {
  api: 'api',
  auth: {
    do: 'something',
  },
};

new Vue({
  render: h => h(App),
  provide: {
    ...myCollection,
    ...myServices,
  },
}).$mount('#app');
// src/helpers/vue.ts

import { VueConstructor } from 'vue';

export function makeInjector<TProvider>() {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  return function<V extends Vue, K extends keyof TProvider>(v: VueConstructor<V>, properties: K[]) {
    return v as VueConstructor<V & Pick<TProvider, K>>;
  };
}

export function makePropertySelector<TProvider>() {
  return function<K extends keyof TProvider>(properties: K[]) {
    return properties;
  };
}
// src/components/my-component.vue

import Vue from 'vue';
import { makeInjector, makePropertySelector } from '../helpers/vue';
import { Collection, myServices } from '../main';

// use a type ...
const collectionPropInjector = makeInjector<Collection>();

// ... or typeof
const serviceInjector = makeInjector<typeof myServices>();

// use propertySelector to reuse across injector and inject
const injectServices = makePropertySelector<typeof myServices>();
const services = injectServices(['auth']);

// add properties from one or many types to this Vue instance ...
export default serviceInjector(collectionPropInjector(Vue, ['dog', 'cat']), services).extend({
  // ... and inject properties as normal here
  inject: ['dog', 'cat', ...services],
  created() {
    console.log(this.dog); // 'dog'
    console.log(this.cat); // 0
    console.log(this.cow); // undefined

    console.log(this.api); // undefined
    console.log(this.auth.do); // 'something'
  },
});

Works with VSCode Intellisense as well.

shivam-deepsource commented 3 years ago

Been facing the same issue with my TypeScript project. I resorted to use $parent and methods in subcomponents to be accessed in descendent component. Would really appreciate if I could use DI with TypeScript.

fgarciajulia commented 2 years ago

A temporary workaround is define it in data as optional. That worked for me.

interface IData {
  accessService: Something
  service?: Service // injected property
}

export default Vue.extend({
  name: 'someComponent',
  inject: ['Service'],
  data(): IData {
    return {
      accessService: this.service?.something
    }
  },
niraj-nagtilak1990 commented 2 years ago

I had to workaround this by defining fake prop for injected property. I am on vue js 2.6.13 version, I hope this gets fixed in new version.

Before

image

After workaround with fake prop

image

XLearner commented 2 years ago

您好,你发送的信息我已成功接收,我会尽快回复您。Lambert Yim