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

[TypeScript / TSX] Make component events type safe, both emitting and listening #1553

Closed
wonderful-panda opened this issue Jul 9, 2020 · 9 comments

Comments

@wonderful-panda
Copy link
Contributor

What problem does this feature solve?

Avoid mistakes around emitting and listening component events

What does the proposed API look like?

Emitting events

// Define event type (see `About Events types` section)
type Events = {
  withoutArgs: [],
  withOneArg: [string],
  withTwoArgs: [string, number]
}

const MyComponent = defineComponent({
  props: { ... }, 
  setup(props, { emit }: SetupContext<Events>) {  // Add type parameter to SetupContext
    const emitEvents = () => {
     /*
      * OK
      */
      emit("withoutArgs");
      emit("withOneArg", "foo");
      emit("withTwoArgs", "foo", 1);

     /*
      * Compile error
      */
      emit("withoutArgs", "foo");
      emit("withOneArgs"); 
      emit("withOneArgs", "foo", 1);
      emit("withOneArgs", 1);
      emit("withTwoArgs", "foo");
      emit("wrongEventName");
    }
    ...
  }
});

Listening events

const ParentComponent = defineComponent({
  setup() {
    return () => (
      <MyComponent
        on={{
           // check event names and argument types at compile type
           withoutArgs: () => ...,
           withOneArg: arg1 => ...
        }}
      />
    )
  }
});

About Events type

There are some candidates about Events type specification.

1. specify argument types only

Pros: Compact
Cons: Argument names can't be specified

type Events = {
  event1: [],
  event2: [string],
  event3: [string, number]
}
2. specify full function signature

Pros: Meaningful argument names can be specified
Cons: Verbose

type Events = {
  event1: () => void,
  event2: (userName: string) => void,
  event3: (id: string, index: number) => void
}
3. hybrid

Pros: Flexible
Cons: Internal types become complicated.

type Events = {
  event1: [],
  event2: [string],
  event3: (id: string, index: number) => void
}
@ChrisShank
Copy link

ChrisShank commented Jul 12, 2020

I think an API for this was already discussed in this RFC and already works in the Vue 3 Beta!

@wonderful-panda
Copy link
Contributor Author

Thank you. I've overlooked it.

@tcastelly
Copy link

Maybe I do something wrong. On a tsx file I can't do something like this:

const customClick = () => {}
<CustomBtn on={{ customClick }} />

I have this error:

Error:(7, 16) TS2322: Type '{ on: {}; }' is not assignable to type 'IntrinsicAttributes & Partial<{}> & Pick<Readonly<{ label: string; } & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "label" | ... 9 more ... | "style">'.
  Property 'on' does not exist on type 'IntrinsicAttributes & Partial<{}> & Pick<Readonly<{ label: string; } & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "label" | ... 9 more ... | "style">'.

Similar error if I try to do this (but works on runtime):

<CustomBtn onCustomClick={customClick} />

@yyx990803
Copy link
Member

yyx990803 commented Oct 29, 2020

We don't have this implemented for typing yet, since it actually relies on the template literal types feature which (I think) is going to be part of TS 4.1.

@tcastelly
Copy link

ok thank you :)

@caohuatao
Copy link

caohuatao commented Nov 16, 2020

<FlowNode
   key={node.id}
   node={node}
   onMessage={message}
   v-slots={ {default: slots.default}}
/>
Error:(103, 11) TS2322: Type '{ key: string | number; node: NodeItem; onMessage: () => void; "v-slots": { default: Slot | undefined; }; }' is not assignable to type 'IntrinsicAttributes & Partial<{}> & Pick<Readonly<{ node: NodeItem; } & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "style" | ... 9 more ... | "class">'.
  Property 'onMessage' does not exist on type 'IntrinsicAttributes & Partial<{}> & Pick<Readonly<{ node: NodeItem; } & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, "style" | ... 9 more ... | "class">'.

Seek a solution!

@tcastelly
Copy link

We don't have this implemented for typing yet, since it actually relies on the template literal types feature which (I think) is going to be part of TS 4.1.

TS 4.1 has been released, I still have the error. Do you think it's possible to use the template literal types feature?

@caohuatao
Copy link

interface IntrinsicAttributes extends ReservedProps {
     [emit: string]: any
}

@tcastelly
Copy link

@caohuatao

Thank you for your help, update the file ./node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts as bellow remove errors.
I guess it's a good workaround to avoid hundred errors :)

I'm looking forward to see a native solution to be able to keep types.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants