2.5.22
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)
Function, and with a default function that returns some value; e.g.,// ...
props: {
fooFn: {
type: Function,
default: () => true,
},
},
// ...
// ...
methods: {
useFooFn(): void {
const bar = this.fooFn();
// ...
},
},
// ...
type FooFn = typeof this.fooFn; // Function
this.fooFn(); // no errors
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
}
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)
}
}
});
@posva Are you using the same TypeScript setup and seeing no compile errors?
no, I'm using the one we have in the repo
Can you try with the setup I posted?
@posva


Found a clue:
I just downgraded vue (and vue-template-compiler) to 2.5.17 and it works fine, as it used to. Then I upgraded both to 2.5.18, and now I see a bunch of compiler errors (including this one) which had never occurred before:
// for Function-type props with a default like `() => false`, `(arg: number) => false`, etc.:
// => TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'boolean | Function' has no compatible call signatures.
// (same error occurs with any return value)
function foo(barEl: HTMLElement) { /* ... */ }
foo(this.$el);
// => TS2345: Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
const bar = this.$el.innerText;
// => TS2339: Property 'innerText' does not exist on type 'Element'.
Seems that:
Function _or_ the return type of their default functionthis.$el has become Element instead of HTMLElementvue/vue-template-compiler 2.5.18+, but not 2.5.17~It may have something to do with "noImplicitThis": true in tsconfig.json; setting it to false removes all those errors (there is one new error in our project after setting it to false but it's to do with a lack of type inference on this.$store, all in one component rather than scattered across the project)~
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.
And reverting https://github.com/vuejs/vue/pull/8809, specifically this change:
diff --git a/types/vue.d.ts b/types/vue.d.ts
index 44a892ead3..3832f2c9e4 100644
--- a/types/vue.d.ts
+++ b/types/vue.d.ts
@@ -21,7 +21,7 @@ export interface CreateElement {
}
export interface Vue {
- readonly $el: HTMLElement;
+ readonly $el: Element;
readonly $options: ComponentOptions<Vue>;
readonly $parent: Vue;
readonly $root: Vue;
...fixes the this.$el defaulting to Element issue.
I'll see if I can write up a PR for a fix without resurfacing the original issues those PRs were trying to solve.
Aaaaand I've realized over the past few days that I am not good enough with TypeScript to figure out how to do this.
Where...
"noImplicitThis": true in tsconfig.json, and...in order to get a prop definition such as this:
// ...
props: {
isValid: {
type: Function,
default: () => true,
}
},
// ...
...to yield a type of:
this.isValid //=> Type: () => boolean
...instead of:
this.isValid //=> Type: boolean | () => boolean
You'd have to edit the PropOptions interface such that:
export interface PropOptions<T=any> {
type?: Prop<T> | Prop<T>[];
required?: boolean;
// default?: T | null | undefined | (() => T | null | undefined);
// I guess...?
default?: Function extends T ? (T | null | undefined) : (T | null | undefined | (() => T | null | undefined));
validator?(value: T): boolean;
}
Unfortunately, that example doesn't work, and the types of other properties on Vue are lost. I've tried a _lot_ of different things over the past few days, but clearly I don't have the expertise to understand exactly how to fix the issue.
I believe, currently, a Function-type prop is a unique case. It is (correct me if I'm wrong), the _only_ prop type that does not have the option of a default "factory" function:
props: {
fnProp1: {
type: Function,
default: () => false, // type of this.fnProp1 should be `() => boolean`
},
fnProp2: {
type: Function,
default: () => (() => false), // type of this.fnProp2 should be `() => (() => boolean)`
},
boolProp1: {
type: Boolean,
default: false, // type of this.boolProp1 should be `boolean`
},
boolProp2: {
type: Boolean,
default: () => false, // type of this.boolProp2 should STILL be `boolean`
},
strProp1: {
type: String,
default: 'hi', // type of this.strProp1 should be `string`
},
strProp2: {
type: String,
default: () => 'hi', // type of this.strProp2 should STILL be `string`
},
// etc.
},
Furthermore, if you want to return an _object_ from the default, it _completely_ skips the function type altogether:
// ...
props: {
returnsAnObject: {
type: Function,
default: () => ({}),
}
},
// ...
// Type SHOULD be `Function`, or `() => {}`, but...
this.returnsAnObject; //=> Type: {}
// ...which is not even the [broken] union `{} | () => {}` type like the other cases
this.returnsAnObject();
// Cannot invoke an expression whose type lacks a call signature.
// Type '{}' has no compatible call signatures.
// ...
I'd rather not keep bumping this unnecessarily, since it's mostly an echo chamber at the moment, but this 2.5.17 to 2.5.18+ patch update breaks our build, necessitates a lot of boilerplate around what used to be correctly-inferred properties on our components, and the causal changes seem to be fairly clear. Any attention or help would be greatly appreciated!
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
Is this issue back on TypeScript 3.6? The following compiles fine on TS 3.5.3 and fails on the latest TS 3.6.3.
Vue: 2.6.10
TypeScript: 3.6.3
import Vue from 'vue';
export default Vue.extend({
props: {
cb: {
type: Function,
default: () => {},
},
},
created() {
this.cb();
}
});
13:10 This expression is not callable.
No constituent of type 'void | Function' is callable.
11 |
12 | created() {
> 13 | this.cb();
| ^
14 | }
15 | });
Removing default: () => {} from cb as well as annotating it with PropOptions<() => void> helps, but this wasn't needed before.
Put a repro here: https://github.com/romansp/vue-typescript-prop-function-default.
May be related to #10455.
@romansp I'm fairly confident that hasn't worked without annotation since Vue 2.5.17. Annotating with PropType<...> (note: _not_ PropOptions<...>) works just fine on Vue 2.6.10 and TypeScript 3.6.3, though.
@kjleitz I'm sure that it does work on TS 3.5.3 and Vue 2.6.10. You can try cloning my repro https://github.com/romansp/vue-typescript-prop-function-default. I just pushed ts-3.5.3 branch where vue serve runs fine.
@romansp Ah, I see, you're not using the same tsconfig.json as in my original example. The fact that you're using "strict": true instead of "noImplicitThis": true fixes it in your ts-3.5.3 branch (we've also switched to using "strict": true since the time this ticket was written; much better!). Even with "strict": true though, if you set Vue back to v2.6.8 it's actually still broken in your ts-3.5.3 branch. It's always been wonky.
Vue v2.6.10 & TS v3.5.3 must be one of those special combinations that don't error for function props 馃し鈥嶁檪 But even in that branch, the "working" case loses type info from this.cb鈥攂etter to use type: Function as PropType<() => void>, instead of a bare type: Function,.
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.
Most helpful comment
if you annotate with the
PropType<>it should work, this was a fix on https://github.com/vuejs/vue/pull/9733there's an PR https://github.com/vuejs/vuejs.org/pull/2068 to update docs