Skip to content

Commit 9952576

Browse files
committed
feat: trying to get components with generic types working
1 parent 5893f3e commit 9952576

16 files changed

+230
-999
lines changed

packages/button/src/Button.svelte

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,31 @@
6363
} from '@smui/common/internal';
6464
import Ripple from '@smui/ripple';
6565
import type { SmuiComponent } from '@smui/common';
66-
import { Element } from '@smui/common';
66+
import { SmuiElement } from '@smui/common';
67+
68+
type TagName = $$Generic<
69+
keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
70+
>;
71+
type Component = $$Generic<SmuiComponent<TagName>>;
72+
type $$Props = svelteHTML.IntrinsicElements[TagName] & {
73+
use?: ActionArray;
74+
class?: string;
75+
style?: string;
76+
ripple?: boolean;
77+
color?: 'primary' | 'secondary';
78+
variant?: 'text' | 'raised' | 'unelevated' | 'outlined';
79+
touch?: boolean;
80+
href?: string | undefined;
81+
action?: string;
82+
defaultAction?: boolean;
83+
secondary?: boolean;
84+
component?: ComponentType<Component>;
85+
tag?: TagName;
86+
};
6787
6888
const forwardEvents = forwardEventsBuilder(get_current_component());
6989
70-
// Remember to update types file if you add/remove/rename props.
90+
// Remember to update $$Props if you add/remove/rename props.
7191
export let use: ActionArray = [];
7292
let className = '';
7393
export { className as class };
@@ -81,14 +101,16 @@
81101
export let defaultAction = false;
82102
export let secondary = false;
83103
84-
let element: SmuiComponent;
104+
let element: Component;
85105
let internalClasses: { [k: string]: boolean } = {};
86106
let internalStyles: { [k: string]: string } = {};
87107
let context = getContext<string | undefined>('SMUI:button:context');
88108
89-
export let component: SmuiComponent = Element;
90-
export let tag =
91-
component === Element ? (href == null ? 'button' : 'a') : null;
109+
export let component: ComponentType<Component> =
110+
SmuiElement as ComponentType<Component>;
111+
export let tag: TagName | undefined = (
112+
component === SmuiElement ? (href == null ? 'button' : 'a') : undefined
113+
) as TagName | undefined;
92114
93115
$: actionProp =
94116
context === 'dialog:action' && action != null
@@ -144,9 +166,7 @@
144166
}
145167
}
146168
147-
export function getElement(): ReturnType<
148-
InstanceType<typeof component>['getElement']
149-
> {
169+
export function getElement() {
150170
return element.getElement();
151171
}
152172
</script>

packages/button/src/Button.types.ts

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,8 @@
1-
import type { ComponentProps, ComponentType } from 'svelte';
2-
import type { SmuiComponent, ElementComponentDev } from '@smui/common';
1+
import type { ComponentProps } from 'svelte';
2+
import type { SmuiComponent, SmuiElement } from '@smui/common';
33
import type Component from './Button.svelte';
44

55
export declare class ButtonComponentDev<
6-
T extends string = 'button',
7-
C extends ComponentType<SmuiComponent> = ComponentType<
8-
ElementComponentDev<T>
9-
>
10-
>
11-
extends Component
12-
implements SmuiComponent
13-
{
14-
/**
15-
* @private
16-
* For type checking capabilities only.
17-
* Does not exist at runtime.
18-
* ### DO NOT USE!
19-
*/
20-
$$prop_def: Omit<
21-
Partial<
22-
svelte.JSX.HTMLAttributes<ReturnType<InstanceType<C>['getElement']>>
23-
>,
24-
| 'use'
25-
| 'class'
26-
| 'style'
27-
| 'ripple'
28-
| 'color'
29-
| 'variant'
30-
| 'touch'
31-
| 'href'
32-
| 'action'
33-
| 'defaultAction'
34-
| 'secondary'
35-
| 'component'
36-
| 'tag'
37-
> &
38-
ComponentProps<Component>;
39-
40-
getElement(): ReturnType<InstanceType<C>['getElement']>;
41-
}
6+
T extends keyof SmuiElementTagNameMap = 'button',
7+
C extends SmuiComponent<T> = SmuiElement<T>
8+
> extends Component<T, C> {}

packages/button/src/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { ComponentType } from 'svelte';
22

33
import Button from './Button.svelte';
4-
import type { ButtonComponentDev } from './Button.types.js';
54
export * from './Button.types.js';
65

76
import GroupComponent from './Group.svelte';
@@ -11,6 +10,6 @@ const Group = GroupComponent as ComponentType<GroupComponentDev>;
1110
import GroupItem from './GroupItem.js';
1211
import { Label, Icon } from '@smui/common';
1312

14-
export default Button as ComponentType<ButtonComponentDev>;
13+
export default Button;
1514

1615
export { Group, GroupItem, Label, Icon };

packages/common/src/CommonIcon.svelte

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,24 @@
2525
2626
import type { ActionArray } from './internal/useActions.js';
2727
import { forwardEventsBuilder, classMap } from './internal/index.js';
28-
import type { SmuiComponent } from './smui.types.js';
29-
import { Element } from './index.js';
28+
import type { SmuiComponent, SmuiElementTagNameMap } from './smui.types.js';
29+
import { SmuiElement } from './index.js';
30+
31+
type TagName = $$Generic<keyof SmuiElementTagNameMap>;
32+
type Component = $$Generic<SmuiComponent<TagName>>;
33+
type OwnProps = {
34+
use?: ActionArray;
35+
class?: string;
36+
on?: boolean;
37+
component?: ComponentType<Component>;
38+
tag?: TagName;
39+
};
40+
type $$Props = {
41+
[P in Exclude<
42+
keyof svelteHTML.IntrinsicElements[TagName],
43+
keyof OwnProps
44+
>]?: svelteHTML.IntrinsicElements[TagName][P];
45+
} & OwnProps;
3046
3147
const forwardEvents = forwardEventsBuilder(get_current_component());
3248
@@ -36,16 +52,17 @@
3652
export { className as class };
3753
export let on = false;
3854
39-
let element: SmuiComponent;
55+
let element: Component;
4056
41-
export let component: ComponentType<SmuiComponent> = Element;
42-
export let tag = component === Element ? 'svg' : null;
57+
export let component: ComponentType<Component> =
58+
SmuiElement as ComponentType<Component>;
59+
export let tag: TagName | undefined = (
60+
component === SmuiElement ? 'svg' : undefined
61+
) as TagName | undefined;
4362
4463
const context = getContext<string | undefined>('SMUI:icon:context');
4564
46-
export function getElement(): ReturnType<
47-
InstanceType<typeof component>['getElement']
48-
> {
65+
export function getElement() {
4966
return element.getElement();
5067
}
5168
</script>
Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,9 @@
1-
import type { ComponentProps, ComponentType } from 'svelte';
2-
import type { SmuiComponent } from './smui.types.js';
3-
import type { ElementComponentDev } from './Element.types.js';
1+
import type { ComponentProps } from 'svelte';
2+
import type { SmuiComponent, SmuiElementTagNameMap } from './smui.types.js';
3+
import type SmuiElement from './SmuiElement.svelte';
44
import type Component from './CommonIcon.svelte';
55

66
export declare class CommonIconComponentDev<
7-
T extends string = 'i',
8-
C extends ComponentType<SmuiComponent> = ComponentType<
9-
ElementComponentDev<T>
10-
>
11-
>
12-
extends Component
13-
implements SmuiComponent
14-
{
15-
/**
16-
* @private
17-
* For type checking capabilities only.
18-
* Does not exist at runtime.
19-
* ### DO NOT USE!
20-
*/
21-
$$prop_def: Omit<
22-
Partial<
23-
svelte.JSX.HTMLAttributes<ReturnType<InstanceType<C>['getElement']>>
24-
>,
25-
'use' | 'class' | 'on' | 'component' | 'tag'
26-
> &
27-
ComponentProps<Component>;
28-
29-
getElement(): ReturnType<InstanceType<C>['getElement']>;
30-
}
7+
T extends keyof SmuiElementTagNameMap = 'svg',
8+
C extends SmuiComponent<T> = SmuiElement<T>
9+
> extends Component<T, C> {}

packages/common/src/CommonLabel.svelte

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,23 @@
2929
3030
import type { ActionArray } from './internal/useActions.js';
3131
import { forwardEventsBuilder, classMap } from './internal/index.js';
32-
import type { SmuiComponent } from './smui.types.js';
33-
import { Element } from './index.js';
32+
import type { SmuiComponent, SmuiElementTagNameMap } from './smui.types.js';
33+
import { SmuiElement } from './index.js';
34+
35+
type TagName = $$Generic<keyof SmuiElementTagNameMap>;
36+
type Component = $$Generic<SmuiComponent<TagName>>;
37+
type OwnProps = {
38+
use?: ActionArray;
39+
class?: string;
40+
component?: ComponentType<Component>;
41+
tag?: TagName;
42+
};
43+
type $$Props = {
44+
[P in Exclude<
45+
keyof svelteHTML.IntrinsicElements[TagName],
46+
keyof OwnProps
47+
>]?: svelteHTML.IntrinsicElements[TagName][P];
48+
} & OwnProps;
3449
3550
const forwardEvents = forwardEventsBuilder(get_current_component());
3651
@@ -39,17 +54,18 @@
3954
let className = '';
4055
export { className as class };
4156
42-
let element: SmuiComponent;
57+
let element: Component;
4358
44-
export let component: ComponentType<SmuiComponent> = Element;
45-
export let tag = component === Element ? 'span' : null;
59+
export let component: ComponentType<Component> =
60+
SmuiElement as ComponentType<Component>;
61+
export let tag: TagName | undefined = (
62+
component === SmuiElement ? 'span' : undefined
63+
) as TagName | undefined;
4664
4765
const context = getContext<string | undefined>('SMUI:label:context');
4866
const tabindex = getContext<number | undefined>('SMUI:label:tabindex');
4967
50-
export function getElement(): ReturnType<
51-
InstanceType<typeof component>['getElement']
52-
> {
68+
export function getElement() {
5369
return element.getElement();
5470
}
5571
</script>
Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,9 @@
1-
import type { ComponentProps, ComponentType } from 'svelte';
2-
import type { SmuiComponent } from './smui.types.js';
3-
import type { ElementComponentDev } from './Element.types.js';
1+
import type { ComponentProps } from 'svelte';
2+
import type { SmuiComponent, SmuiElementTagNameMap } from './smui.types.js';
3+
import type SmuiElement from './SmuiElement.svelte';
44
import type Component from './CommonLabel.svelte';
55

66
export declare class CommonLabelComponentDev<
7-
T extends string = 'span',
8-
C extends ComponentType<SmuiComponent> = ComponentType<
9-
ElementComponentDev<T>
10-
>
11-
>
12-
extends Component
13-
implements SmuiComponent
14-
{
15-
/**
16-
* @private
17-
* For type checking capabilities only.
18-
* Does not exist at runtime.
19-
* ### DO NOT USE!
20-
*/
21-
$$prop_def: Omit<
22-
Partial<
23-
svelte.JSX.HTMLAttributes<ReturnType<InstanceType<C>['getElement']>>
24-
>,
25-
'use' | 'class' | 'component' | 'tag'
26-
> &
27-
ComponentProps<Component>;
28-
29-
getElement(): ReturnType<InstanceType<C>['getElement']>;
30-
}
7+
T extends keyof SmuiElementTagNameMap = 'span',
8+
C extends SmuiComponent<T> = SmuiElement<T>
9+
> extends Component<T, C> {}

0 commit comments

Comments
 (0)