-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
feat: improve types for createEventDispatcher
#7224
feat: improve types for createEventDispatcher
#7224
Conversation
9a34d89
to
22aa51d
Compare
I just saw that this repo is using TypeScript Are the following points something the Svelte-Team would like to improve?
pinging @dummdidumm since he probably knows the answers to my questions :) |
In #6770 I have the need, too, to bump the TS version. Adding more types is welcome, too. We just have to ensure we don't introduce new syntax in the public API that isn't supported in older versions of TS |
Maybe we could try downlevel-dts if we want new typescript features in public API. |
Thanks for your response. I'll try to add some more types that are compatible with |
@dummdidumm I saw you are upgrading to version 4.0 |
@dummdidumm I have created a wrapper Since these changes will be really time-consuming, I'm asking if I should proceed with this PR? This
I'm not sure if the Svelte-Team wants that. |
4c48f95
to
5491871
Compare
I now have split my changes so this PR just includes the types for The changes made to the Nevertheless this PR could introduce a breaking change since a new (totally valid) error-message could show up in some codebases. |
If you want to benefit from the stronger types for You just have to create a import 'svelte'
declare module 'svelte' {
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never
type ExtractObjectValues<Object extends Record<any, any>> = Object[keyof Object]
type ConstructDispatchFunction<
EventMap extends Record<string, any>,
EventKey extends keyof EventMap
> = EventMap[EventKey] extends never | null
? (type: EventKey) => void
: null extends EventMap[EventKey]
? (type: EventKey, detail?: EventMap[EventKey]) => void
: (type: EventKey, detail: EventMap[EventKey]) => void
type CreateDispatchFunctionMap<EventMap> = {
[Key in keyof EventMap]: ConstructDispatchFunction<EventMap, Key>
}
type EventDispatcher<EventMap extends Record<string, any>> = UnionToIntersection<
ExtractObjectValues<CreateDispatchFunctionMap<EventMap>>
>
/**
* @example
* ```ts
* const dispatch = createEventDispatcher<
* click: number // define the type of the event's `detail`
* loaded: never // use `never` if the event should not contain any payload
* 'custom-event': string | null // use an union type and add `null` if the payload is optional
* >()
* ```
*/
export declare function createEventDispatcher<
EventMap extends Record<string, any> = any
>(): EventDispatcher<EventMap>
} |
i wonder if we can introduce different type definitions using |
Sounds promising. Could be a bit painful to link everything well together. What I could not find out from reading the docs is: can you use this to enhance single typedefinitions or do you have to include all typedefinitions in that specific path? Maybe I find some time to try this out next week. |
Per my comment on #7324 (comment), I think we can get that PR in soon, so it shouldn't be a blocker. Mind updating the merge conflict on this one? |
@ivanhofer is attempting to deploy a commit to the Svelte Team on Vercel. A member of the Team first needs to authorize it. |
@benmccann done. Tested it with follwing cases and it looks good! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for bringing it up to date!
Strictly speaking this is a breaking change because people could rely on the optional behavior currently. So I think this needs to land in version 4. To safe this PR from needing another rebase we should probably merge this in the v4 branch right before all the other monorepo conversion stuff etc
Do you change the |
There is none yet, details of the process are not fleshed out yet. |
I was able to simplify the types (and they also read better when using them now) taking the learnings from #7442. I added tests, but we need to check them manually for now since we need strict mode for checking them, and it pull in too much other files when doing that through the CLI which we would need to convert to strict types. For Svelte 5 this should be solved. |
--------- Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
I'm giving a try to these new stricter types for createEventDispatcher and it seems that using given: const dispatch = createEventDispatcher<{
dismiss: never;
}>();
dispatch('dismiss') I get the following error:
and using void, null or undefined (the example from the migration guide uses Should I create a different issue for this? $ npx envinfo --npmPackages svelte,svelte-check,@sveltejs/kit,typescript,tslib,vite --binaries
Binaries:
Node: 16.20.1 - /usr/bin/node
npm: 8.19.4 - /usr/bin/npm
npmPackages:
@sveltejs/kit: ^1.21.0 => 1.21.0
svelte: ^4.0.3 => 4.0.3
svelte-check: ^3.4.4 => 3.4.4
tslib: ^2.6.0 => 2.6.0
typescript: ^5.1.6 => 5.1.6
vite: ^4.3.9 => 4.3. |
I'm sorry, the migration instruction was a bit outdated, you should use |
thanks for the response, the problem with null is that is allows to pass and with never: btw, docs in the migration guide correctly tell us to use |
Passing |
This PR introduces a breaking change: the generic that can be passed to
createEventDispatcher
was improved, which means you can now specify which of the events have required arguments and which don't:How to migrate
These changes might break your code if the generic is written in a way that assumes the old more lax typings.
never
YourType | null
(this might catch some errors in your code on the usage side where event listeners assume that the type is always present)Note that you need to enable
strict
mode for TypeScript to take full advantage of the stricter types. Withoutstrict
mode, required arguments are still optional (this is a TypeScript limitation).Before submitting the PR, please make sure you do the following
[feat]
,[fix]
,[chore]
, or[docs]
.Tests
npm test
and lint the project withnpm run lint
This PR adds some more sophisticated typings for the
createEventDispatcher
function.Problem
The current implementation marks the
detail
parameter always as optional:And when accessing it in a parent component, It would only be marked as
string
but it actually would bestring | undefined
Solution
edited: it seems that
detail
is defaulting tonull
so I updated the exampleThis PR solves the issue in as follows.
If the type is marked:
never
: nodetail
parameter required/allowedstring
: thedetails
parameter is required and must have the type ofstring
null
e.g.string | null
: thedetails
parameter is optional and can be of typestring | null
Please see following examples:
The lines annotated with
// @ts-expect-error
are not valid and will throw a TypeScript error.Since this PR adds a few helper types to the
lifecycle.ts
file, I would suggest to move the types to a.d.ts
file. Since there are no such types in the reop yet, I don't know how the Svelte-team want to handle this.Regarding tests:
There are no
types tests
in the repo yet, so I propose to create atypes
folder in thetest
directory and paste the example above to a file inside that directory. The test command then would need to be extended with atsc --noEmit
command on this folder.What do you think?