From 8040ff465173023fb9620c8462264562a33ca991 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Sun, 20 Sep 2020 17:43:30 +0200 Subject: [PATCH 1/9] TS: Typing Props/Slots/Events --- text/ts-typing-props-slots-events.md | 181 +++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 text/ts-typing-props-slots-events.md diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md new file mode 100644 index 0000000..8e2af1c --- /dev/null +++ b/text/ts-typing-props-slots-events.md @@ -0,0 +1,181 @@ +- Start Date: 2020-09-20 +- RFC PR: +- Svelte Issue: https://github.com/sveltejs/language-tools/issues/442 + +# TypeScript: Typing Props/Events/Slots + Generics + +## Summary + +Provide possibilites for TypeScript users to strongly type a Svelte component's props/events/slots, including generics. For that, we introduce reserved interfaces named `ComponentProps`, `ComponentEvents`, `ComponentSlots`, `ComponentDef` to type parts or all of them at once. We also introduce ` + + + +``` + +Now you want to ensure that listeing to anything else than `on:own`/`on:click` throws a type error. For that you use the new ` + + + +``` + +Now you add one event which comes from a dispatcher mixin: + +```html + + + + + +``` + +In this case `strictEvents` will not work anymore because we cannot know that `mixinDispatch` dispatches events. So now you use the `ComponentEvents` interface. + +```html + + + + + +``` + +### Typing Slots + +This works the same as for typing events. + +```html + + + +``` + +### Typing Props + +This works the same as for typing events. You probably won't use that because it's essentially doing the type work twice. + +```html + +``` + +### Generics + +You want to specify some generic connection between props/slots/events. For this you use the ` + +... +``` + +### ComponentDef + +If you want to type all at once, because you like to have the definition on one place or want to better define a generic relationship, you can use the `ComponentDef` interface. + +```html + + +... +``` + +You can also leave out props/events/slots in that interface depending on what you actually use in your component. + +### Summary + +As you can see, there would be several options to achieve the same. You can use `ComponentDef` to type all at once, or you can mix and match the other possibilities to only type part of it. The drawback is that there is more than one way to achieve the same goal. But only having `ComponentDef` would be too much typing overhead of you only want to specifically type parts of the component. In general, props, slots and their types are already inferable quite nicely at this point. Only generics and events are where you really would need this. + +### Implementation hurdles + +We would need to make sure that we can provide some meaningful errors if the definition and the actual types don't match. So if someone types `ComponentSlots` as `{foo: boolean;}` but does ``, we must highlight that. I have not looked closely into how this can be achieved yet because I want to first have agreement on the API. + +## How we teach this + +For users: Enhance docs. For intellisense devs: A more formal specification outlining the details. + +## Drawbacks + +- This will only work for TS users +- Uncanny-valley-stuff for generics +- Reserved interface names could collide with existing ones, but I think that's rare. It's also only a breaking change for the language-tools because it does not affect the core of Svelte +- Many ways to achieve the same goal (interface/generics combinations) + +## Alternatives + +- Don't do anything and say "well, there are some limits". VueJS for example also cannot deal with generics as far as I know. +- Only provide parts of this solution: `strictEvents` and `generics`, but from the interfaces only `ComponentDef`, and tell people "if you want to type it, type it all". + +## Unresolved questions + +- Interface wording ok? +- Attribute wording ok? From 08f49214d341306cf1f6e486836e8719a25e6aca Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Sun, 20 Sep 2020 17:49:54 +0200 Subject: [PATCH 2/9] typos, fine tune --- text/ts-typing-props-slots-events.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md index 8e2af1c..817d193 100644 --- a/text/ts-typing-props-slots-events.md +++ b/text/ts-typing-props-slots-events.md @@ -37,7 +37,7 @@ You start with one event which is from your own typed `createEventDispatcher` an ``` -Now you want to ensure that listeing to anything else than `on:own`/`on:click` throws a type error. For that you use the new ` + +... +``` + ### ComponentDef If you want to type all at once, because you like to have the definition in one place or want to better define a generic relationship, you can use the `ComponentDef` interface. @@ -149,6 +171,34 @@ If you want to type all at once, because you like to have the definition in one ... ``` +### ComponentDef alternative: Namespace + +As an alternative to the `ComponentDef` interface, one could use a namespace and put the interfaces inside it. That would make refactoring easier if you for example start of with typing only the events but want to add more typings to slots later on. This would come at the cost of uncanny-valley-stuff for defining the generics. + +```html + + +... +``` + ### Summary As you can see, there would be several options to achieve the same. You can use `ComponentDef` to type all at once, or you can mix and match the other possibilities to only type part of it. The drawback is that there is more than one way to achieve the same goal. But only having `ComponentDef` may be too much typing overhead of you only want to specifically type parts of the component. In general, props, slots and their types are already inferable quite nicely at this point. Only generics and events are where you really would need this. From b51f459805cb8c30f6efeebd8b7dd32eee1750d8 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 27 Jan 2021 10:16:56 +0100 Subject: [PATCH 4/9] Another option for generics --- text/ts-typing-props-slots-events.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md index ed6bb63..767fa89 100644 --- a/text/ts-typing-props-slots-events.md +++ b/text/ts-typing-props-slots-events.md @@ -154,6 +154,23 @@ You use a new reserved interface called `ComponentGenerics` and do the typing on ... ``` +#### Option 3 +You use new reserved type called `ComponentGeneric`. + +```html + +``` + ### ComponentDef If you want to type all at once, because you like to have the definition in one place or want to better define a generic relationship, you can use the `ComponentDef` interface. From 384d912dbfa294c35a061a586328afec8817e765 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 20 Feb 2021 16:07:25 +0100 Subject: [PATCH 5/9] mark some proposals as discarded --- text/ts-typing-props-slots-events.md | 53 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md index 767fa89..ebe8055 100644 --- a/text/ts-typing-props-slots-events.md +++ b/text/ts-typing-props-slots-events.md @@ -6,7 +6,7 @@ ## Summary -Provide possibilites for TypeScript users to strongly type a Svelte component's props/events/slots, including generics. For that, we introduce reserved interfaces named `ComponentProps`, `ComponentEvents`, `ComponentSlots`, `ComponentDef` to type parts or all of them at once. We also introduce ` - -... ``` -##### Option 2 -You use a new reserved interface called `ComponentGenerics` and do the typing on it, not declaring any properties on it. +##### Discarded alternative 1 +You use a new ` + +... ``` -### ComponentDef +Discarded because it is invalid TypeScript without additional transformations. + + +### ComponentDef (likely discarded) If you want to type all at once, because you like to have the definition in one place or want to better define a generic relationship, you can use the `ComponentDef` interface. @@ -188,7 +194,9 @@ If you want to type all at once, because you like to have the definition in one ... ``` -### ComponentDef alternative: Namespace +This is likely not a good idea because you then can achieve typing a component in multiple ways, which introduces maintenance overhead for implementations. + +#### Discarded ComponentDef alternative: Namespace As an alternative to the `ComponentDef` interface, one could use a namespace and put the interfaces inside it. That would make refactoring easier if you for example start of with typing only the events but want to add more typings to slots later on. This would come at the cost of uncanny-valley-stuff for defining the generics. @@ -216,6 +224,8 @@ As an alternative to the `ComponentDef` interface, one could use a namespace and ... ``` +This is discarded because it provides no real benefit over the three-seperate-interfaces-solution. + ### Summary As you can see, there would be several options to achieve the same. You can use `ComponentDef` to type all at once, or you can mix and match the other possibilities to only type part of it. The drawback is that there is more than one way to achieve the same goal. But only having `ComponentDef` may be too much typing overhead of you only want to specifically type parts of the component. In general, props, slots and their types are already inferable quite nicely at this point. Only generics and events are where you really would need this. @@ -233,12 +243,13 @@ For users: Enhance docs. For intellisense devs: A more formal specification outl - This will only work for TS users - Uncanny-valley-stuff for generics - Reserved interface names could collide with existing ones, but I think that's rare. It's also only a breaking change for the language-tools because it does not affect the core of Svelte -- Many ways to achieve the same goal (interface/generics combinations) +- If we implement `SvelteComponentDef`, then there are multiple ways to achieve the same goal (interface/generics combinations) ## Alternatives - Don't do anything and say "well, there are some limits". VueJS for example also cannot deal with generics as far as I know. -- Only provide parts of this solution: `strictEvents` and `generics`, but from the interfaces only `ComponentDef`, and tell people "if you want to type it, type it all". +- Only provide parts of this solution: `strictEvents` and generics, and from the interfaces only `ComponentDef`, and tell people "if you want to type it, type it all". +- Shorter interface name alternatives: `Props` / `Events` / `Slots` / `Generic`. More likely that they clash with existing definitions? ## Unresolved questions From 03307e7eb3029d0d8bd1c4ea4b7e5f3783b60c5b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 20 Feb 2021 16:10:23 +0100 Subject: [PATCH 6/9] enhance generic example --- text/ts-typing-props-slots-events.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md index ebe8055..3e17c14 100644 --- a/text/ts-typing-props-slots-events.md +++ b/text/ts-typing-props-slots-events.md @@ -128,6 +128,11 @@ You use new reserved type called `ComponentGeneric`. type T = ComponentGeneric; // extends boolean type X = ComponentGeneric; // any + + // you can use generics inside the other interfaces + interface ComponentSlots { + default: { aSlot: T } + } export let array1: T[]; export let item1: T; From 683fe6d12051b1c9107cfe76cfb90af5efe2fc43 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 27 Feb 2021 19:44:51 +0100 Subject: [PATCH 7/9] update names to use $$-Svelte-style-wording --- text/ts-typing-props-slots-events.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md index 3e17c14..f364e63 100644 --- a/text/ts-typing-props-slots-events.md +++ b/text/ts-typing-props-slots-events.md @@ -6,7 +6,7 @@ ## Summary -Provide possibilites for TypeScript users to strongly type a Svelte component's props/events/slots, including generics. For that, we introduce reserved interfaces named `ComponentProps`, `ComponentEvents`, `ComponentSlots`. We also introduce a concept for generics and a ` @@ -107,7 +107,7 @@ This works the same as for typing events. You probably won't use that because it ```html ``` +If you define `$$Props`, all possible props need to be part of it. If you use `$$props` or `$$restProps` then that does _not_ widen the type, still only those defined in `$$Props` are allowed. + ### Generics You want to specify some generic connection between props/slots/events. For example you have a component which has an input prop `item`, and an event called `itemChanged`. You want to use this component for arbitrary kinds of item, but you want to make sure that the types for `item` and `itemChanged` are the same. Generics come in handy then. You can read more about them on the [official TypeScript page](https://www.typescriptlang.org/docs/handbook/generics.html). From bfb14dc56a70ec6ddafebf2242b8e1500e06a032 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 26 May 2023 15:06:04 +0200 Subject: [PATCH 9/9] Update ts-typing-props-slots-events.md --- text/ts-typing-props-slots-events.md | 33 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/text/ts-typing-props-slots-events.md b/text/ts-typing-props-slots-events.md index 1f584ed..24cc366 100644 --- a/text/ts-typing-props-slots-events.md +++ b/text/ts-typing-props-slots-events.md @@ -122,45 +122,46 @@ If you define `$$Props`, all possible props need to be part of it. If you use `$ You want to specify some generic connection between props/slots/events. For example you have a component which has an input prop `item`, and an event called `itemChanged`. You want to use this component for arbitrary kinds of item, but you want to make sure that the types for `item` and `itemChanged` are the same. Generics come in handy then. You can read more about them on the [official TypeScript page](https://www.typescriptlang.org/docs/handbook/generics.html). #### Solution -You use new reserved type called `$$Generic`. + +You use a new ` + +... ``` ##### Discarded alternative 1 -You use a new ` - -... ``` -Discarded because it is invalid TypeScript without additional transformations. +Discarded because it is invalid TypeScript when using advanced typings such as `const T extends string` and it's harder to read. The `generics` attribute reads exactly like the generics on a function. ##### Discarded alternative 2 You use a new reserved interface called `$$Generics` and do the typing on it, not declaring any properties on it.