vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
46.68k stars 8.19k forks source link

How to import interface for defineProps #4294

Closed Otto-J closed 1 year ago

Otto-J commented 3 years ago

update: please see:

7394

https://github.com/vuejs/core/issues/4294#issuecomment-1316097560


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 commented 3 years ago

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.

yibird commented 3 years ago

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

dwanl commented 3 years ago

我也遇到了这个坑

MatthewTt commented 3 years ago

same problem

phasetri commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

@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: https://github.com/wheatjs/vite-plugin-vue-type-imports/issues/4

nborko commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

wait

LinusBorg commented 2 years ago

@nborko Would it be possible for you to work with actual props objects instead of TS interfaces? Or does that raise other issues in your workflow? i.e. this works fine:

// Props.ts
export const defaultProps = {
  age: {
    type: Number,
    required: true,
  },
  job: {
    type: String,
    default: "Employee",
  },
} as const;
<script setup lang="ts">
import { defaultProps } from './Props'
const props = defineProps({
  ...defaultProps,
  name: String,
  address: {
    required: true,
  }
})
</script>
Bildschirmfoto 2022-01-09 um 16 29 45

If you need an actual type representation of the props as they are required from the parent, those can be extracted from these objects with a bit of type trickery.

mareszhar commented 2 years ago

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.

Thank you so much for making this, @wheatjs ❤️ ! In the future, I'd also like to help fix this kind of issues. Do you have any pointers on what one should learn to be able to build or improve a plugin like yours ✨?

nborko commented 2 years ago

@LinusBorg That does work but negates the benefit of type checking the props using interfaces. Since the import in question is effectively a mixin, the props can be used in several places, and the interface may contain simple string literal types or something a lot more complex. It's helped me find a bunch of usage errors from the old 2.x code base that has worked in general, but typing everything has helped me see a lot of potential issues that I would have not otherwise found by inspection.

I've been playing around with casting individual properties as Prop<>, and that does help, as does const props: SomeInterface = defineProps() . The type annotations on the interface had to change, however, since props with defaults can no longer be "undefined" in terms of typing.

Deep diving into the implementation of the compiler functions and macros has helped me resolve some of these issues, but my conclusion is that they promise much but deliver little in actual usage. I really like the idea of them and the syntactical sugar they provide, but at this time defineProps<T> and withDefaults are far too limited to be of any practical use for me.

LinusBorg commented 2 years ago

Thanks for the response. I'm unsure about the verdict though.

Is using a props object with additional PropType<> annotation able to do what you need or is something still missing? Is that a route that gives you typings for all props in the way you need?

nborko commented 2 years ago

Yes, I was probably too harsh, as the macros probably do work well for a good 95%+ of the people using the expected pattern. I was initially pretty frustrated when my meticulously developed type interfaces didn't work, and finally stumbled on this issue. I also didn't want to support two different coding paradigms in a single project, but in the end this may save me some work since I can copy/paste my old props definitions and annotate them instead of rewriting it from scratch.

PropType<> isn't exported, but Prop<T, D> is (which includes PropType), which seems to be working so far. It doesn't provide a super fine-grained type checking on the prop definition itself (notably, when type is an array), but it does type checking everywhere it gets used, and that's a fair enough compromise for me.

LinusBorg commented 2 years ago

PropType<> isn't exported, but Prop<T, D> is (which includes PropType),

PropType certainly is exported by 'vue', I use it daily.

nborko commented 2 years ago

PropType<> isn't exported, but Prop<T, D> is (which includes PropType),

PropType certainly is exported by 'vue', I use it daily.

Huh, so it is. I stand corrected.

Shinigami92 commented 2 years ago

I'm now also affected by this issue and trying to workaround for 2 hours now without luck :slightly_frowning_face:

I want to extend a quasar QBtn like so

<script setup lang="ts">
import type { QBtnProps } from 'quasar';

defineProps<QBtnProps>();
</script>

<template lang="pug">
QBtn(
  v-bind="$attrs",
  color="primary",
  flat,
  icon-right="mdi-chevron-right"
)
</template>

Just want to forward all the attributes and have TypeScript completion with Volar, but I don't want to redefine every single attribute that QBtn already knows. Any hints how I can workaround that?

LinusBorg commented 2 years ago

How about:

<script setup lang="ts">
import { QBtn } from 'quasar';

defineProps(QBtn.props);
</script>
Shinigami92 commented 2 years ago

How about:

<script setup lang="ts">
import { QBtn } from 'quasar';

defineProps(QBtn.props);
</script>

Sadly that doesn't help at all, due to QBtn.props is of type any

Shinigami92 commented 2 years ago

Not sure right now, but I may found the solution

<script lang="ts">
import type { QBtnProps } from 'quasar';
import { defineComponent } from 'vue';

export default defineComponent<QBtnProps>({});
</script>

<template lang="pug">
QBtn(color="primary", flat, icon-right="mdi-chevron-right")
</template>

This is not setup, but at least it seems it will automatically just forward the props :eyes:

andreataglia commented 2 years ago

any update on this? it's quite a burden. Thanks!

richardvanbergen commented 2 years ago

You can kind-of work around it by exporting only the parts of the interface you need. In this case I only really care about RouterLinkProps['to'], I can add the other parts of RouterLink as I need them.

<script setup lang="ts">
import { RouterLink } from 'vue-router'
import type { RouterLinkProps } from 'vue-router'
defineProps<{
  to: RouterLinkProps['to']
}>()
</script>

<template>
  <RouterLink :to="to" custom v-slot="{ href, navigate }">
    <a
      v-bind="$attrs"
      :href="href"
      @click="navigate"
      class="inline-flex items-center justify-center p-0.5 overflow-hidden font-medium text-gray-900 rounded-lg bg-gradient-to-br from-green-400 to-blue-600 group group-hover:from-green-400 group-hover:to-blue-600 hover:text-white dark:text-white focus:ring-4 focus:ring-green-200 dark:focus:ring-green-800"
    >
      <span
        class="px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-opacity-0"
      >
        <slot />
      </span>
    </a>
  </RouterLink>
</template>
Miofly commented 2 years ago

any update?

aislanmaia commented 2 years ago

For me this problem should get all the importance for the Vue community, because it completelly defeats the purpose of using Vue at all (with Typescript, of course). The framework was rewritten with Typescript and a simple Prop cannot be typed with actual useful types (and interfaces) that everyone use!! It's insane!!!

Vue 3.0 should not be released without this feature !!!!!

Shinigami92 commented 2 years ago

We are already on Vue 3.2.29 :rofl:

LinusBorg commented 2 years ago

For me this problem should get all the importance for the Vue community, because it completelly defeats the purpose of using Vue at all (with Typescript, of course). The framework was rewritten with Typescript and a simple Prop cannot be typed with actual useful types (and interfaces) that everyone use!! It's insane!!!

You either seem to misunderstand the problem or propel it way out of proportion.

First off, you can fully and properly type your props in all cases by providing a props object instead of a type interface:

defineProps({
  name: Object as PropType<User>
})

This way of using an actual props object and PropType:

import Props from './sharedProps.ts'

defineProps(Props)

So, if you want to use a type interface instead of a props object, please understand that this is a new, additional feature, an optimization that, for now, has a few limitation. But they might not be as big as you think:

// This works
import { User } from './types.ts'
defineProps<{
  user: User // but you will get no *runtime* warning if you were passing a string instead of an object
}>()

So you can fully type your pops with a TS interface - but you have to write it out like above, you can't import the whole interface:

// This doesn't work
import { PropfileProps } from './types.ts'
defineProps<PropfileProps>()

We understand that this last thing is a challenge i.e. for library authors, and we have the goal of removing this limitation in the future.

But achieving that requires a certain level of complexity in the compiler that needs to be evaluated and implemented carefully and thoroughly, not least because it could also have a significant performance impact during dev if our SFC compiler has to look up and construct types from maybe multiple levels of external source files.

None of this hinders you to fully type your props with a normal props object, which is the "normal" way that worked ever since Vue 3.0 got released and before defineProps() became a thing in 3.2

mesqueeb commented 2 years ago

@LinusBorg

when we define the props via the JS way (to be able to import/export) like so:

export const mySharedProps = {
  user: {
    type: Object as PropType<User | undefined>,
    default: undefined
  }
}

is there any way to then extract a type that would look like this:

export type MySharedProps = {
  user?: User | undefined
}

Any advice on how to achieve this would be greatly appreciated! 🍻

LinusBorg commented 2 years ago

@mesqueeb This is possible with a bit of type magic, yes.

import {
  ExtractPropTypes,
 ExtractDefaultPropTypes,
} from 'vue'
import { SetOptional } from 'type-fest'

export type ExternalProps<T extends Record<string | number | symbol, {}>> 
  = SetOptional<ExtractPropTypes<T>, keyof ExtractDefaultPropTypes<T>>

Usage:

import { ExternalProps } from './typeUtils.ts'
import { PropTypes } from 'vue'
// Example custom types
interface User {
  name: string
}
interface Post {
  title: string
}

const mySharedProps = {
  user: {
    type: Object as PropType<User>,
    default: () => ({ name: 'Tom' })
  },
  post: {
    type: Object as PropType<Post>,
    required: true,
  },
} as const // <= THIS IS NECESSARY TO DETECT REQUIRED PROPS CORRECTLY!

export type MySharedProps = ExternalProps<typeof mySharedProps>

//This is the resulting type:
type MySharedProps = {
  user?: User | undefined // has default value internally, but from parent's perspective it's optional
  post: Post // required
}

A little bit on how this works:

ExtractPropTypes gives you almost what you want, but the resulting interface has props with a default value marked as required. This is because this interface is the internal props interface - this.$props, where props with default values are guaranteed to be present.

So we need to make these optional. How?

For the last step I use SetOptional from the amazing type-fest collection of useful types, but I'm sure there's a SO answer out there that explains how to make properties on an interface optional if you don't want to add another dependency.

Also, yes - I think it would make real sense to have this in the core types.

nagman commented 2 years ago

I solved it by simply passing types as a sub-object:

~/composables/useHomePage.ts

export function useHomePage() {
  // fetches content from API with useStatic
  // returns a Ref<{
  //   seo: { title: string, description: string },
  //   introHero: { title: string, text: string, ... }
  //   ...
  // }>
}

// This type returns what's in the Ref<> returned by useHomePage
export type TUseHomepage = NonNullable<
  ReturnType<typeof useHomepage>['value']
>;

~/pages/index.vue

<script lang="ts" setup>
import IntroHero from '../components/home/intro-hero.vue';
import { useHomepage } from '~/composables/useHomePage';

const homepage = useHomepage();
</script>

<template>
  <main v-if="homepage">
    <IntroHero :data="homepage.introHero" />
  </main>
</template>

~/components/home/intro-hero.vue

<script setup lang="ts">
import type { TUseHomepage } from '~/composables/homepage';

// defineProps<TUseHomepage['introHero']>(); // doesn't work, throws "defineProps is not defined"
defineProps<{ data: TUseHomepage['introHero'] }>(); // works :D
</script>

So instead of passing props with v-bind, I pass them to :data, which is quite convenient in fact because it allows me to separate context between data and other props.

As I use the apollo cli to generate types for my GraphQL requests, I then can use the generated types in my components. I have my data props, which are strings and images returned by the API, and I have my "logic" props, like :visible, :active, and so forth.

sadeghbarati commented 2 years ago

third party usecase typescript - using volar nested namespace and interface not working in SFC <script lang="ts"> and <script setup lang="ts">

<script setup lang="ts">
import DataTables from 'datatables.net'

interface Props {
  options?: DataTables.Settings; //error: 'DataTables' only refers to a type, but is being used as a namespace here
  options?: DataTables["Settings"]; //error: 'DataTables' only refers to a type, but is being used as a namespace here
  columns?: DataTables.ColumnSettings[]; //error: 'DataTables' only refers to a type, but is being used as a namespace here
  checkbox?: boolean;
  footer?: boolean;
  constant?: boolean;
}
</script>

these exact Props works well in external type or interface file but imports from other files are not supported


Update: Problem Above Solved with extended jquery-extended.d.ts be sure type included in tsconfig.json

/// <reference types="jquery"/>
/// <reference types="datatables.net"/>

interface JQueryStatic {
    DataTable(opts?: DataTables.Settings): DataTables.Api;
    dataTable: DataTables.StaticFunctions;
}
Miofly commented 2 years ago

setup 语法糖中的这种语法什么时候可以支持

    interface Props extends IconProps {
        value?: string;
        showAction?: boolean;
        animation?: boolean;
        actionStyle?: object;
        actionText?: string;
    }
howlwindy commented 2 years ago
// Icon.vue
<script lang="ts">
// summary: add comment above some line
export enum EIconSize {
  XS,
  SM,
  MD,
  LG,
  XL
}
// interface must not be imported which use for defineProps
export interface IIconProps {
  // ... if next line is custom type, add comment here, i don't know why, but it worked
  size: EIconSize
  title: string
}
</script>
<script setup lang="ts">
// ... if next line is defineProps, add comment here, i don't know why, but it worked
const props = withDefaults(defineProps<IIconProps>(), {
  size: EIconSize.MD,
  title: 'Vue'
})
</script>
// X.vue
<script setup lang="ts">
import { EIconSize, IIconProps } from './Icon.vue'
</script>
Miofly commented 2 years ago

setup 语法糖中的这种语法什么时候可以支持

  interface Props extends IconProps {
      value?: string;
      showAction?: boolean;
      animation?: boolean;
      actionStyle?: object;
      actionText?: string;
  }

不加?控制台还会报错,Partial 这种 ts 方法还不能用,头疼啊

jinmayamashita commented 2 years ago

~How about using keyword extends?~

// sharedProps.ts
export interface SharedProps {
  foo: string;
  bar?: number;
}
<script lang="ts" setup>
import { SharedProps } from "./sharedProps.ts";

interface Props extends SharedProps {}
const props = defineProps<Props>(); // The type props.foo can be inferred to be a string
</script>

☝🏼 It didn't work. I can infer the type, but it seems to be an empty object when compiling.

edison1105 commented 2 years ago

~How about using keyword extends?~

// sharedProps.ts
export interface SharedProps {
  foo: string;
  bar?: number;
}
<script lang="ts" setup>
import { SharedProps } from "./sharedProps.ts";

interface Props extends SharedProps {}
const props = defineProps<Props>(); // The type props.foo can be inferred to be a string
</script>

☝🏼 It didn't work. I can infer the type, but it seems to be an empty object when compiling.

this is also not surpport yet.

tobychidi-zz commented 2 years ago

We are still waiting for a fix?

Miofly commented 2 years ago

any update on this

tony-gm commented 2 years ago
// Icon.vue
<script lang="ts">
// summary: add comment above some line
export enum EIconSize {
  XS,
  SM,
  MD,
  LG,
  XL
}
// interface must not be imported which use for defineProps
export interface IIconProps {
  // ... if next line is custom type, add comment here, i don't know why, but it worked
  size: EIconSize
  title: string
}
</script>
<script setup lang="ts">
// ... if next line is defineProps, add comment here, i don't know why, but it worked
const props = withDefaults(defineProps<IIconProps>(), {
  size: EIconSize.MD,
  title: 'Vue'
})
</script>
// X.vue
<script setup lang="ts">
import { EIconSize, IIconProps } from './Icon.vue'
</script>

How did you find this? It's pretty interesting. with this comment, it's work. But without this comment or change the comment content, it will compile failed. WHY???

yousefamar commented 2 years ago

@tony-gm I've come across something like this before -- I think the root problem wasn't the lack of a comment, but rather:

<script setup lang="ts"> does not work

<script lang="ts" setup> works

Is that the same for you?

MvdDonk commented 2 years ago

@yousefamar I'm having similar problems as the ticket starter but changing the "setup" attribute doesn't fix it for me. The following gives the error: "Syntax Error: TypeError: Cannot read property 'content' of null" image

Adding the interface to the local file works: image

Also, wrapping the interface (loaded from external file) in a property fixes the error message: image

Screenshot of Models/FormData.ts: image

Is wrapping the interface in a property by design or is this a bug? Or am I missing something...?

howlwindy commented 2 years ago
// Icon.vue
<script lang="ts">
// summary: add comment above some line
export enum EIconSize {
  XS,
  SM,
  MD,
  LG,
  XL
}
// interface must not be imported which use for defineProps
export interface IIconProps {
  // ... if next line is custom type, add comment here, i don't know why, but it worked
  size: EIconSize
  title: string
}
</script>
<script setup lang="ts">
// ... if next line is defineProps, add comment here, i don't know why, but it worked
const props = withDefaults(defineProps<IIconProps>(), {
  size: EIconSize.MD,
  title: 'Vue'
})
</script>
// X.vue
<script setup lang="ts">
import { EIconSize, IIconProps } from './Icon.vue'
</script>

How did you find this? It's pretty interesting. with this comment, it's work. But without this comment or change the comment content, it will compile failed. WHY???

when i wanna export interface/enum/type from .vue to .ts, so try lot of ways, now i give up, e.g at below is worked.

<script lang="ts">
// enum here worked
export enum E {
  // ...
}
</script>
<script setup lang="ts">
import E from x.enum
// enum here error
export enum E {
  // ...
}
export interface I {
  // ...
}
// for defineProps interface must in setup
export interface IProps {
  e: E
  i: I
  // ...
}
// instead of withDefault's default values, because they probably are not literal
const DEFAULT: Required<IProps> = {
  // ...
}
const props = defineProps<IProps>()
const computed = (() => deepMerge(DEFAULT, props))
</script>
Miofly commented 2 years ago

any update?

Codebreaker101 commented 2 years ago

Just in case anyone needs @LinusBorg example without type-fest lib:

import { ExtractPropTypes, ExtractDefaultPropTypes } from 'vue';

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type PartialBy<T, K> = Omit<T, K> & Partial<T>;
type Writeable<T> = { -readonly [P in keyof T]: T[P] };

export type TypeFromProps<T> = Writeable<PartialBy<ExtractPropTypes<T>, keyof ExtractDefaultPropTypes<T>>>;
aislanmaia commented 2 years ago
export type TypeFromProps<T> = Writeable<PartialBy<ExtractPropTypes<T>, keyof ExtractDefaultPropTypes<T>>>;

How to use it inside component when defining props ?

Codebreaker101 commented 2 years ago

@aislanmaia for example

import { defineComponent, PropType, h } from 'vue';
import { ExtractPropTypes, ExtractDefaultPropTypes } from 'vue';

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type PartialBy<T, K> = Omit<T, K> & Partial<T>;
type Writeable<T> = { -readonly [P in keyof T]: T[P] };

export type TypeFromProps<T> = Writeable<PartialBy<ExtractPropTypes<T>, keyof ExtractDefaultPropTypes<T>>>;

// ======

const props = {
  title: {
    type: String as PropType<string>,
    required: true,
  },
  padding: {
    type: Boolean as PropType<boolean>,
    required: false,
    default: true,
  },
  negative: {
    type: Boolean as PropType<boolean>,
    required: false,
  },
  disabled: {
    type: Boolean as PropType<boolean>,
    required: false,
  },
} as const;

export type Props = TypeFromProps<typeof props>;

export default defineComponent({
  props: props,
  setup(props) {
    console.log(props);
    return [
      h('h1'),
    ];
  }
});