vue: Function-type props broken in TypeScript

Version

2.5.22

Reproduction link

https://jsfiddle.net/keegan_openbay/gehkx7pf/10/
https://jsfiddle.net/keegan_openbay/018rs3ae/11/

(More explanation in the fiddle, but keep in mind that JSFiddle doesn’t show TS errors)

Steps to reproduce

  1. Declare a prop of type Function, and with a default function that returns some value; e.g.,
// ...
  props: {
    fooFn: {
      type: Function,
      default: () => true,
    },
  },
// ...
  1. Try to use that function elsewhere in your component options; e.g.,
// ...
  methods: {
    useFooFn(): void {
      const bar = this.fooFn();
      // ...
    },
  },
// ...

What is expected?

type FooFn = typeof this.fooFn; // Function
this.fooFn(); // no errors

What is actually happening?

type FooFn = typeof this.fooFn; // boolean | Function
this.fooFn();
// Cannot invoke an expression whose type lacks a call signature.
// Type 'boolean | Function' has no compatible call signatures.

Vue version: 2.5.22 TypeScript version: 3.0.3

tsconfig.json:

{
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es7", "dom"],
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "baseUrl": "./app/javascript",
    "noImplicitThis": true
  },
  "include": [
    "app/javascript/**/*.ts",
    "app/javascript/**/*.tsx",
    "app/javascript/**/*.vue"
  ],
  "exclude": [
    "**/*.spec.ts",
    "node_modules"
  ],
  "compileOnSave": false
}

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 25
  • Comments: 18 (3 by maintainers)

Commits related to this issue

Most upvoted comments

if you annotate with the PropType<> it should work, this was a fix on https://github.com/vuejs/vue/pull/9733

const Example = Vue.extend({
	template: `
  	<button @click="doSomethingWithFoo()">
    	<slot></slot>
    </button>
  `,

  props: {
    // original issue
    fooFn: {
      type: Function as PropType<()=>string>,
      default: () => { return 'hey this is the default return value'; },
    },


     returnsAnObject: {
      type: Function as PropType<()=>object>,
      default: () => ({}),
    }
  },

  methods: {
    doSomethingWithFoo(): void {
      const obj = this.returnsAnObject(); //obj is object
    	const bar = this.fooFn(); // bar is string
  
      alert(bar);
    },
  },
});

there’s an PR https://github.com/vuejs/vuejs.org/pull/2068 to update docs

This is still broken, even with "strict": true; can’t use a default for a function-type prop. A more complete example:

const ComponentWithFunctionProps = Vue.extend({
  props: {
    functionProp: {
      type: Function,
      default: () => true,
    },
    functionPropWithBooleanReturnType: {
      type: Function as PropType<() => boolean>,
      default: () => true,
    },
    booleanProp: {
      type: Boolean,
      default: true,
    },
    booleanPropWithFunctionDefault: {
      type: Boolean,
      default: () => true,
    },
  },
  methods: {
    test(): void {
      // ERROR!
      // (property) functionProp: boolean | Function
      // -------------------------------------------
      // This expression is not callable.
      //   No constituent of type 'boolean | Function' is callable.ts(2349)
      this.functionProp();

      // ERROR!
      // (property) functionPropWithBooleanReturnType: boolean | (() => boolean)
      // -----------------------------------------------------------------------
      // This expression is not callable.
      //   Not all constituents of type 'boolean | (() => boolean)' are callable.
      //     Type 'false' has no call signatures.ts(2349)
      this.functionPropWithBooleanReturnType();

      // const foo: boolean
      const foo = this.booleanProp;

      // const bar: boolean
      const bar = this.booleanPropWithFunctionDefault;
    },
  },
});

I submitted a fix for this in https://github.com/vuejs/vue/pull/11223.

Reverting https://github.com/vuejs/vue/pull/8537, specifically this change:

diff --git a/types/options.d.ts b/types/options.d.ts
index cc58affe6a..25eb8a0fdf 100644
--- a/types/options.d.ts
+++ b/types/options.d.ts
@@ -133,7 +133,7 @@ export type PropValidator<T> = PropOptions<T> | Prop<T> | Prop<T>[];
 export interface PropOptions<T=any> {
   type?: Prop<T> | Prop<T>[];
   required?: boolean;
-  default?: T | null | undefined | (() => object);
+  default?: T | null | undefined | (() => T | null | undefined);
   validator?(value: T): boolean;
 }

…fixes the Function-type prop issue.

I tried adding the test case to the project and couldn’t reproduce:

Vue.extend({
  props: {
    isValid: {
      type: Function,
      default: () => true,
    }
  },
  methods: {
    useFooFn(): void {
      const bar = this.isValid()
      alert(bar)
    }
  }
});