-
-
Notifications
You must be signed in to change notification settings - Fork 420
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
This expression is not callable. Type '{}' has no call signatures - Dynamic slots + Generic #3141
Comments
@johnsoncodehk is this supported by volar ATM? Because I think both are the same thing 🤔 I also tried doing something like this for my actual code but doesn't seems to work: { [name in `col-${RowKey<Row>}`]: (_: { row: Row; colIndex: number }) => any } |
This doesn't seem to be a Volar issue, you can reproduce it in script. <script lang="ts" setup generic="Row extends Record<string, unknown>">
import { objectKeys } from '../utils';
import { aTableProps, aTableSlots } from './meta';
const props = defineProps(aTableProps<Row>())
const _slots = aTableSlots<Row>(
objectKeys(props.rows[0] || {}),
)
defineSlots<typeof _slots>()
for (const col of props.cols) {
_slots[`header-${col.name}`]?.({ col }); // Type '{}' has no call signatures.ts(2349)
}
</script> |
Thanks for your inspection. May I know how to resolve this because according to me I'm generating correct object in aTableSlots? Awaiting your response. |
Strange thing is if you comment Extending my previous comment, I guess I'm generating correct object but it looks like there's issue with the generated type. |
@jd-solanki Hi, it seems to be this is a TypeScript's limit: type Slots<Foo extends string> = {
readonly "before-table": (_: any) => null;
} & Record<`header-${Foo}`, (_: { col: any }) => any>;
function foo<Foo extends string>(
slots: Slots<Foo>,
key: `header-${Foo}`
) {
const slot = slots[key];
// @ts-expect-error This expression is not callable. Type '{}' has no call signatures.ts(2349)
slot?.({ col: 1 });
} If you simplify your code: type Slots<Foo extends string> = Record<`header-${Foo}`, (_: { col: any }) => any>;
function foo<Foo extends string>(
slots: Slots<Foo>,
key: `header-${Foo}`
) {
const slot = slots[key];
slot?.({ col: 1 });
} All works well. Maybe you should create a issue to TypeScript. |
Closed this issue because it is an upstream issue, if you need any other help, or if you find any other volar issues, please feel free to raise them below. |
Might be similar: vuejs/core#8352 |
I'm not quite sure what's going on, but I found that in your latest example, the slots and key are controlled by the same generic And in your original example, actually |
Can we improve something at volar side considering this kind of common usage? vuejs/core#7982 (comment) vuejs/core#8352 What's your thoughts on reopening and renaming this issue? |
I tried doing that and it still gives // meta.ts
export function aTableSlots<Row extends Record<string, unknown>>(colKeys: RowKey<Row>[]) {
return ({
// If I comment below line then error in SFC is gone 🤔
'before-table': (_: any) => null,
...colKeys.reduce((a, colKey) => (
{ ...a, [`header-${colKey}`]: Function }
), {} as { [K in `${RowKey<Row>}-cell`]: (props: { item: RowKey<Row>; }) => any; }),
}) as const
} <!-- script part is same -->
<template>
<div class="wrapper">
<div v-for="row in props.rows">
<header v-for="(col, index) in props.cols" :key="index">
<slot :name="`${col.name}-cell`" :col="col"></slot>
<!-- This expression is not callable. Type '{}' has no call signatures. -->
</header>
</div>
</div>
</template> In my reproduction and this example, if you comment out |
Here's video demo: Screen.Recording.2023-05-19.at.3.26.12.PM.mov |
Hi, @jd-solanki. In your example: update vue to 3.3.4 change function aTableSlots export function aTableSlots<Row extends Record<string, unknown>>(colKeys: RowKey<Row>[]) {
return ({
// If I comment below line then error in SFC is gone 🤔
'before-table': (_: any) => null,
...colKeys.reduce((a, colKey) => (
{ ...a, [`header-${colKey}`]: Function }
), {} as Record<`header-${RowKey<Row>}`, (_: { col: ATablePropColumn<Row> }) => any>),
}) as const
} to export function aTableSlots<Row extends Record<string, unknown>>(colKeys: RowKey<Row>[]) {
return ({
// If I comment below line then error in SFC is gone 🤔
'before-table': (_: any) => null,
...colKeys.reduce((a, colKey) => (
{ ...a, [`header-${colKey}`]: Function }
), {} as { [K in keyof Row as `header-${K & string}`]: (_: { col: ATablePropColumn<Row> }) => any } ),
}) as const
} And then change <slot :name="`header-${col.name}`" :col="col"></slot> to <slot :name="`header-${String(col.name)}`" :col="col"></slot> Can you see weather it fix your problem? |
I would strongly recommend not using I would recommend switching to a type type aTableSlots<T extends Record<string, unknown>> = {
[K in keyof T as `header-${K & string}`]: (_: {
col: ATablePropColumn<T>
}) => any
} & {
['before-table']?: (_: { a: string }) => any
} The reason being To use this new const slots = defineSlots<aTableSlots<Row>>() full example: <script lang="ts" setup generic="Row extends { [K: string]: any}">
interface ATablePropColumn<Row extends Record<string, unknown>> {
name: RowKey<Row>
}
const props = defineProps<{
rows: Row[]
cols: ATablePropColumn<Row>[]
}>()
type RowKey<Row extends Record<string, unknown>> = keyof Row & string
type aTableSlots<T extends Record<string, unknown>> = {
[K in keyof T as `header-${K & string}`]: (_: {
col: ATablePropColumn<T>
}) => any
} & {
['before-table']?: (_: { a: string }) => any
}
const slots = defineSlots<aTableSlots<Row>>()
for (const col of props.cols) {
const slot = slots[`header-${String(col.name)}`]
slot?.({ col })
}
</script>
<template>
<div class="wrapper">
<div v-for="row in rows">
<header v-for="(col, index) in cols" :key="index">
<slot :name="`header-${String(col.name)}`" :col="col"></slot>
<!-- This expression is not callable. Type '{}' has no call signatures. -->
</header>
</div>
</div>
</template> |
@pikax I found your demo and @jd-solanki 's demo also works without remove |
import { defineProps, defineSlots } from "vue";
type RowKey<Row extends Record<string, unknown>> = keyof Row & string;
interface ATablePropColumn<Row extends Record<string, unknown>> {
name: RowKey<Row>;
}
function fakeComponent<Row extends { [K: string]: any }>() {
const props = defineProps<{
rows: Row[];
cols: ATablePropColumn<Row>[];
}>();
type aTableSlots<T extends Record<string, unknown>> = {
[K in keyof T as `header-${K & string}`]: (_: { col: ATablePropColumn<T> }) => any;
} & {
["before-table"]?: (_: { a: string }) => any;
};
const slots = defineSlots<aTableSlots<Row>>();
for (const col of props.cols) {
const slot = slots[`header-${String(col.name)}`];
slot?.({ col });
}
}
fakeComponent(); |
Oh, you are right. |
Old responseHey @pikax and I'll try to go with full TS like below: import { OptionalKeysOf } from 'type-fest'
export interface AlertProps {
rows: unknown[],
icon?: string
appendIcon?: string
dismissible?: boolean
modelValue?: boolean
}
export const alertPropsDefault = {
appendIcon: 'close',
} satisfies Pick<AlertProps, OptionalKeysOf<AlertProps>> (above is just playground component and file is Before refactoring the codebase 4th time I just want to comfirm that,
Final out of context questions: Thanks. I'm using runtime code (JS) because I have to loop over object to pass the slots to the child like this. |
@xiaoxiangmoe Can we remove upstream label? |
Can you see weather it fix your problem? #3141 (comment) Is there any bug should fix in volar? |
Hi @xiaoxiangmoe I was busy with some other stuff. I'll let you know once soon. |
Hi @xiaoxiangmoe 👋🏻 It works 💚 I wanted to know is this some kind of hack to workaround volar or TS limitation. I'm also creating new component based on this component and passing slots down to this component. Will this create any troublem because I'm having type issue in another component that uses it but I'm unable to identify if it's from volar or my side. Thanks. |
This is not hack to workaround volar, it just some workaround for TypeScript. |
I wonder performing that change will make type as Are you interested in checking the another component (build on top of mentioned component) error after perfoming mentioned changes (that solves the type error for that component though)? Regards. |
Fixed by vuejs/core#8374 |
Hi 👋🏻
I was playing with the generic components feature and stumbled upon this issue where I get a type error on the slot like below:
Reproduction: https://github.com/jd-solanki/volar-vue-playground/blob/generic-component-type/src/bar/Bar.vue
The text was updated successfully, but these errors were encountered: