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

How to import interface for defineProps #4294

Closed
Otto-J opened this issue Aug 10, 2021 · 153 comments · Fixed by vue-macros/vue-macros#126 or #7394
Closed

How to import interface for defineProps #4294

Otto-J opened this issue Aug 10, 2021 · 153 comments · Fixed by vue-macros/vue-macros#126 or #7394

Comments

@Otto-J
Copy link
Contributor

Otto-J commented Aug 10, 2021

update:
please see:
#7394

#4294 (comment)


Version

3.2.1

Reproduction link

https://github.com/Otto-J/vue3-setup-interface-errors/blob/master/src/components/HelloWorld.vue

Steps to reproduce

clone: git clone
start: yarn && yarn dev
open: master/src/components/HelloWorld.vue
modify: import interface from './types'

What is expected?

no error

What is actually happening?

[@vue/compiler-sfc] type argument passed to defineProps() must be a literal type, or a reference to an interface or literal type.


in chinese:
我想把 props的interface抽出去,但是会报错,如果interface组件里定义的就正常渲染
in english:
I want to extract the interface of props, but an error will be reported. If the interface component is defined, it will render normally

@yyx990803
Copy link
Member

Relevant RFC section

Currently complex types and type imports from other files are not supported. It is theoretically possible to support type imports in the future.

We'll mark it as an enhancement for the future.

@yyx990803 yyx990803 added the ✨ feature request New feature or request label Aug 10, 2021
@yibird
Copy link

yibird commented Aug 17, 2021

希望Vue团队能早日支持,现在使用vue3.2 defineProps()为Props定义单个单个类型还可以,若涉及到联合或交叉等复杂类型 defineProps就会编译错误, 这对于类型扩展是一件非常糟糕的事情

@dwanl

This comment has been minimized.

@MatthewTt

This comment has been minimized.

@phasetri
Copy link

phasetri commented Oct 14, 2021

I have found a workaround in my case (#4758) that allows you to provide typing to your props (or its nested members) with an interface, even if the interface is imported from another file. The workaround is to rename your interface to something else.

import { Post as PostRenamed } from '@/models/forum/PostDef';

interface Props {
  Args: {Post: PostRenamed };
}
const props = withDefaults(defineProps<Props>(), {});

I noticed that you have to do this renaming workaround if the interface name appears anywhere within your <template>, even if the name doesn't necessarily correspond to your interface.

@languanghao
Copy link

I also find a workaround, just use defineProps(ComplexType) instead of defineProps<ComplexType>. Even the ComplexType is imported from other file, it works fine.

@cdvillard
Copy link

cdvillard commented Nov 16, 2021

Despite a fairly similar setup to @phasetri's issue, I'm finding that the properties of the interface I'm trying to import resolve as attrs instead of props, which breaks what I see as expected and predictable functionality.

Control example:

<script setup lang="ts">
	interface IThingie {
		serial: string,
		id: string,
		startDate: Date | null,
		endDate: Date | null
	}

	const props = withDefaults(defineProps<IThingie>(),{});
</script>

// In vue-devtools (values from route params)
props
    {
        "id": "123",
        "serial": "asdfa",
        "startDate": null,
        "endDate" null
    }

@phasetri's solution:

<script setup lang="ts">
	import { IThingie as InterfaceRenamed } from './types';

	interface Props {
		Args: { IThingie: InterfaceRenamed }
	}
	const props = withDefaults(defineProps<Props>(),{});
</script>

// In vue-devtools (values from route params)
props
    {
        "Args": "undefined"
    }
    
attrs
    endDate: null
    id: 123
    serial: "asdfa"
    startDate: null

In my team's particular case, we want to keep the interface(s) outside of the components and use them as generics. In that way, we'd be able to import them elsewhere such as services as needed. We can't seem to export them from a separate <script lang="ts"> section either when using <script setup lang="ts"> as we run into a compilation error (I'll submit an issue for that if there isn't one yet).

I guess all of this is to say a few things:

  • that the renaming trick shouldn't be called a workaround specifically for importing interfaces
  • to pose the question of whether or not exporting the interface from the Vue component itself would provide a similar effect to exporting from a types.ts file, if we could get around the compilation error caused by two <script lang="ts"> tags, one with setup`
  • and to advocate that this change be treated with a bit more urgency than that of an enhancement for the next version.

That last one comes with some ignorance surrounding the difficulties of compiling Typescript from within Vue and the core team's release cadence, so I respect any disagreement with that request. It's just that, as a user, it does feel irksome that I can't import an interface from a file using a syntactical mode that's supposed to make Typescript adoption simpler.

@TylerOliver
Copy link

To follow up on @cdvillard 's commentary -- In a world where there are many packages interacting with each other, such as Jest testing and Storybook's stories, being able to maintain a reusable interface that can be imported in our .vue components, and our other .ts files, makes a massive difference in our ability to adopt.

If it's possible to see this on a roadmap, that would greatly help with enterprise adoption. I love the simplicity and elegance of Vue, and in particular the new typescript features, but this is a major sticking point for us.

I appreciate all the hard work, I really do. But this is a bigger issue than it sounds.

@wheatjs
Copy link

wheatjs commented Dec 1, 2021

If you are using Vite, you can use this plugin https://github.com/wheatjs/vite-plugin-vue-type-imports as a patch until this is officially supported.

@TylerOliver
Copy link

TylerOliver commented Dec 3, 2021

@wheatjs , I gave the plugin a try before posting, but I couldn't get it working. Let me try again and see if I can at least post an issue with a basic recreation. I'm not sure if part of the issue had to do with running the compat build at the time, but that's no longer the case. I appreciate that you've made this though!

Created this issue on the plugin repo: wheatjs/vite-plugin-vue-type-imports#4

@nborko
Copy link

nborko commented Jan 5, 2022

I've gone all-in on Vue 3, TypeScript and the Composition API. However, the restriction of requiring object literals and interfaces being defined in the SFC source severely limits reuse.

I am attempting to define a common props interface in a composable module to replace the need for a mixin, extending Props interfaces as in #4989, and using mergeProps to merge an object with default values via withDefaults(). As I soon learned, that won't work. So the only solution is to either go crawling back to mixins and the Options API, or repeat literal code in dozens of files, inviting a maintenance nightmare.

Unless anyone has a 3rd option, for which I'm open to suggestions. The vite plugin mentioned above does not solve these issues for me.

@invokermain
Copy link

This severely hinders wrapping 3rd party components via type script, as you have to copy and paste the interfaces into your SFC, greatly increasing maintenance burdens and destroying readability. Unless there is an easier way to do this?

<script setup lang="ts">
import { Datepicker, DatePickerProps } from 'vue3-date-time-picker'

defineProps<DatePickerProps>()
defineEmits(Datepicker.emit)
</script>

<template>
  <div class="inputBox">
    <datepicker
      v-bind="$props"
      class="..."
    />
  </div>
</template>

e.g. the above is impossible currently.

@mesqueeb
Copy link

mesqueeb commented Jan 8, 2022

Is there any way we can donate for specific issues? Or maybe open an issue hunt? I'd love to put in some extra donations specifically towards this ticket.

@webpig

This comment has been minimized.

@liulich3ng

This comment was marked as abuse.

@FedericoBiccheddu
Copy link

PR got merged 3 days ago: #8083

@edison1105
Copy link
Member

closed via #8083

@dvjeshka
Copy link

dvjeshka commented Apr 22, 2023

how to import interfaсe for defineEmits?

@Miofly
Copy link

Miofly commented Apr 23, 2023

can not get OtherProps

type OtherProps = Pick<InputProps, 'suffixIcon' | 'prefixIcon' | 'clearIcon'>

export interface SearchProps extends OtherProps {
  modelValue?: string | number;
  placeholder?: string;
  clearable?: boolean;
  width?: string | number;
  popoverCfg?: PopoverProps;
  visible: boolean
}

@so1ve
Copy link
Member

so1ve commented May 11, 2023

can not get OtherProps

type OtherProps = Pick<InputProps, 'suffixIcon' | 'prefixIcon' | 'clearIcon'>

export interface SearchProps extends OtherProps {
  modelValue?: string | number;
  placeholder?: string;
  clearable?: boolean;
  width?: string | number;
  popoverCfg?: PopoverProps;
  visible: boolean
}

TS complex types are not supoorted


Update:

Probably I am wrong. Some built-in types such as Pick is supported. Maybe extends is not supported huh
?

@rbecheras
Copy link

Hi @Miofly well you should open a new issue because this one is closed. But you can link this one to from the new issue

@johnnyshankman
Copy link

johnnyshankman commented May 16, 2023

@Miofly open a new issue bc I too am having this issue. If I import the interface it does not work. If I define the interface inside the same file as the component, no issue.

My current workaround is:

// `export class MProps extends Vue {  }`
// `export const MOptionsProps: PropsDefinition = { ... classic prop options definition }`
import { MProps, MOptionsProps } from '@/exports/MProps';
const props: Readonly<MProps> = defineProps(MOptionsProps) as Readonly<MProps>;

@DiedeGu
Copy link

DiedeGu commented Jun 8, 2023

@nborko

I am attempting to define a common props interface in a composable module to replace the need for a mixin, extending Props interfaces as in #4989, and using mergeProps to merge an object with default values via withDefaults().

I am still not able to set defaults through a reference and opened a issue for it. Am I correct in thinking it is not possible yet or am I doing something wrong?

@boid-com
Copy link

boid-com commented Aug 7, 2023

what's the latest on this issue?

@underdoeg
Copy link

I works now. I think since vue 3.3

@hou-moliy
Copy link

这个问题现在有办法解决了吗?我还是遇到这样的问题

@so1ve
Copy link
Member

so1ve commented Aug 21, 2023

@hou-moliy 更新到3.3…

@rbecheras
Copy link

English please :-)

@OliverRC
Copy link

I do not see how the PR that addressed this issue actually resolves the underlying problem. I believe this is marked as closed incorrectly by the Github PR process.

@so1ve
Copy link
Member

so1ve commented Sep 13, 2023

@OliverRC It should work now.

@sadeghbarati
Copy link

@so1ve can you check this link? head to Button.vue, extending external type is not working, or maybe I'm not doing the right thing

Vue SFC playground for import external types for defineProps

@so1ve
Copy link
Member

so1ve commented Sep 14, 2023

Hi @sadeghbarati, The type you imported extends HTMLAttributes, which extends another interface that extends a type that requires a type parameter. Thus Vue cannot resolve the type.
image

@OliverRC
Copy link

I can confirm that it looks to be working on 3.3
Vue SFC playground - working minimal example

@tombohub
Copy link

Hi @sadeghbarati, The type you imported extends HTMLAttributes, which extends another interface that extends a type that requires a type parameter. Thus Vue cannot resolve the type. image

Then how can we wrap native button html element? How to use ButtonHTMLAttributes?

@so1ve
Copy link
Member

so1ve commented Sep 14, 2023

@tombohub I'm working on an unplugin: https://github.com/so1ve/unplugin-vue-complex-types which uses typescript compiler's API to resolve such types. But it is not useable now since it has very very huge performance issues.

@zjjjjjjjjjjd
Copy link

what about vue2.7?

@rbecheras
Copy link

what about vue2.7?

It won't happen in 2.7

You need to migrate.

Note: vue 2.x is end of life at the end of the year. Don't expect any major change now.

@github-actions github-actions bot locked and limited conversation to collaborators Oct 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Status: Done