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

Add types Props<T>, Events<T> and Slots<T> which provide types for components #7259

Open
turbosheep44 opened this issue Dec 1, 2022 · 3 comments · May be fixed by #9556
Open

Add types Props<T>, Events<T> and Slots<T> which provide types for components #7259

turbosheep44 opened this issue Dec 1, 2022 · 3 comments · May be fixed by #9556
Labels
has PR A pull request has already been submitted to solve the issue scope: types ✨ feature request New feature or request

Comments

@turbosheep44
Copy link

What problem does this feature solve?

In order to enhance Typescript support, I would like to easily be able to extract the props, event and slots which a component supports. This feature would be very useful for creating better developer tools.

What does the proposed API look like?

Consider the following component:

<script setup lang="ts">
defineEmits<{
  (event: 'change', value: string): void
}>()

withDefaults(
  defineProps<{
    name: string,
    disabled: boolean,
  }>(),
  {
    disabled: false,
  }
)
</script>

<template>
  <div>
    <slot name="heading" />
  </div>
  <div>
    <slot />
  </div>
</template>

The proposed API would allow developers to do the following:

import MyComponent from './MyComponent.vue'

type P = Props<typeof MyComponent>   // { name: string, disabled: boolean? }
type E = Events<typeof MyComponent>  // { (event: 'change', value: string): void }
type S = Slots<typeof MyComponent>   // 'default' | 'heading'

Currently, I am using the following definitions for Props<T>, Events<T> and Slots<T> but I think they are fragile and could be made better.

import { ComponentOptions, ComponentOptionsMixin, ComputedOptions, InjectionKey, MethodOptions } from 'vue'

/** evaluates to `true` if `X` and `Y` are the same type, otherwise evaluates to `false` */
type Equals<X, Y> = (<T>() => T extends X ? 0 : 1) extends <T>() => T extends Y ? 0 : 1 ? true : false

/** extracts keys from `T` which are `readonly` */
type ReadonlyKeys<T> = {
  [Ro in {
    [K in keyof T]: Equals<Pick<T, K>, Readonly<Pick<T, K>>> extends true ? K : never
  }[keyof T]]: T[Ro]
}

export type Renderable = ComponentOptions<any> & (abstract new (...args: any) => any)

export type Props<T extends Renderable> = Pick<InstanceType<T>['$props'], _PropKeys<T>>
type _PropKeys<T extends Renderable> = keyof (T extends ComponentOptions<infer P> ? ReadonlyKeys<P> : never)

export type Events<T extends Renderable> = T extends ComponentOptions<
  never,
  unknown,
  unknown,
  ComputedOptions,
  MethodOptions,
  ComponentOptionsMixin,
  ComponentOptionsMixin,
  infer E
>
  ? keyof E
  : never

export type Slots<T extends Renderable> = _Slots<InstanceType<T>['$slots']>
type _Slots<S> = keyof {
  [K in keyof S as S[K] extends CallableFunction ? K : never]: S[K]
}
@turbosheep44 turbosheep44 added the ✨ feature request New feature or request label Dec 1, 2022
@theguriev
Copy link

This is awesome. Would be cool to have it as a native helper type.

@so1ve
Copy link
Member

so1ve commented Sep 30, 2023

Try vue-component-type-helpers

@pikax
Copy link
Member

pikax commented Nov 29, 2023

There's a PR to introduce these type helpers, if you have more type helpers you think might be useful, please let me know

@pikax pikax added the has PR A pull request has already been submitted to solve the issue label Nov 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has PR A pull request has already been submitted to solve the issue scope: types ✨ feature request New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants