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

Revert typed SvelteComponent, add SvelteComponentTyped instead #5738

Merged
merged 5 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ It's the last "What's new in Svelte" of the year and there's lots to celebrate!

1. `$$props`, `$$restProps`, and `$$slots` are all now supported in custom web components (**3.29.5**, [Example](https://svelte.dev/repl/ad8e6f39cd20403dacd1be84d71e498d?version=3.29.5)) and `slot` components now support spread props: `<slot {...foo} />` (**3.30.0**)
2. A new `hasContext` lifecycle function makes it easy to check whether a `key` has been set in the context of a parent component (**3.30.0** & **3.30.1**, [Docs](https://svelte.dev/docs#hasContext))
3. `SvelteComponent` is now typed which makes it easier to add typed classes that extend base Svelte Components. Component library and framework authors rejoice! An example: `export class YourComponent extends SvelteComponent<{aProp: boolean}, {click: MouseEvent}, {default: {aSlot: string}}> {}` (**3.30.0**, [RFC](https://github.com/sveltejs/rfcs/pull/37))
3. There is now a new `SvelteComponentTyped` class which makes it easier to add strongly typed components that extend base Svelte components. Component library and framework authors rejoice! An example: `export class YourComponent extends SvelteComponentTyped<{aProp: boolean}, {click: MouseEvent}, {default: {aSlot: string}}> {}` (**3.30.0**, [RFC](https://github.com/sveltejs/rfcs/pull/37))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this broken to begin with? Otherwise this is a breaking change, no? The syntax extends SvelteComponent<...> { ... } ceases to work, right?

Copy link
Member Author

@dummdidumm dummdidumm Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That syntax was introduced in 3.30 but reverted in 3.31 because it introduced a breaking change. const a: typeof SvelteComponent = ASubclassOfSvelteComponent threw errors which it did not before. So yes, the syntax extends SvelteComponent<...> { ... } ceases to work, but it only worked for a few days / in 3.30.0.

4. Transitions within `{:else}` blocks should now complete successfully (**3.29.5**, [Example](https://svelte.dev/repl/49cef205e5da459594ef2eafcbd41593?version=3.29.5))
5. Svelte now includes an export map, which explicitly states which files can be imported from its npm package (**3.29.5** with some fixes in **3.29.6**, **3.29.7** and **3.30.0**)
6. `rollup-plugin-svelte` had a new [7.0.0 release](https://github.com/sveltejs/rollup-plugin-svelte/blob/master/CHANGELOG.md). The biggest change is that the `css` option was removed. Users who were using that option should add another plugin like `rollup-plugin-css-only` as demonstrated [in the template](https://github.com/sveltejs/template/blob/5b1135c286f7a649daa99825a077586655051649/rollup.config.js#L48)
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export {
hasContext,
tick,
createEventDispatcher,
SvelteComponentDev as SvelteComponent
SvelteComponentDev as SvelteComponent,
SvelteComponentTyped
} from 'svelte/internal';
14 changes: 7 additions & 7 deletions src/runtime/internal/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,19 @@ if (typeof HTMLElement === 'function') {
};
}

export class SvelteComponent<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any
> {
/**
* Base class for Svelte components. Used when dev=false.
*/
export class SvelteComponent {
$$: T$$;
$$set?: ($$props: Partial<Props>) => void;
$$set?: ($$props: any) => void;

$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}

$on<K extends Extract<keyof Events, string>>(type: K, callback: (e: Events[K]) => void) {
$on(type, callback) {
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);

Expand All @@ -234,7 +234,7 @@ export class SvelteComponent<
};
}

$set($$props: Partial<Props>) {
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
Expand Down
107 changes: 86 additions & 21 deletions src/runtime/internal/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,57 @@ export function validate_slots(name, slot, keys) {
}
}

export interface SvelteComponentDev<
type Props = Record<string, any>;
export interface SvelteComponentDev {
$set(props?: Props): void;
$on(event: string, callback: (event: any) => void): () => void;
$destroy(): void;
[accessor: string]: any;
}
/**
* Base class for Svelte components with some minor dev-enhancements. Used when dev=true.
*/
export class SvelteComponentDev extends SvelteComponent {
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$prop_def: Props;

constructor(options: {
target: Element;
anchor?: Element;
props?: Props;
hydrate?: boolean;
intro?: boolean;
$$inline?: boolean;
}) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error("'target' is a required option");
}

super();
}

$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn('Component was already destroyed'); // eslint-disable-line no-console
};
}

$capture_state() {}

$inject_state() {}
}

// TODO https://github.com/microsoft/TypeScript/issues/41770 is the reason
// why we have to split out SvelteComponentTyped to not break existing usage of SvelteComponent.
// Try to find a better way for Svelte 4.0.

export interface SvelteComponentTyped<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
Expand All @@ -107,12 +157,42 @@ export interface SvelteComponentDev<
$destroy(): void;
[accessor: string]: any;
}

export class SvelteComponentDev<
/**
* Base class to create strongly typed Svelte components.
* This only exists for typing purposes and should be used in `.d.ts` files.
*
* ### Example:
*
* You have component library on npm called `component-library`, from which
* you export a component called `MyComponent`. For Svelte+TypeScript users,
* you want to provide typings. Therefore you create a `index.d.ts`:
* ```ts
* import { SvelteComponentTyped } from "svelte";
* export class MyComponent extends SvelteComponentTyped<{foo: string}> {}
* ```
* Typing this makes it possible for IDEs like VS Code with the Svelte extension
* to provide intellisense and to use the component like this in a Svelte file
* with TypeScript:
* ```svelte
* <script lang="ts">
* import { MyComponent } from "component-library";
* </script>
* <MyComponent foo={'bar'} />
* ```
*
* #### Why not make this part of `SvelteComponent(Dev)`?
* Because
* ```ts
* class ASubclassOfSvelteComponent extends SvelteComponent<{foo: string}> {}
* const component: typeof SvelteComponent = ASubclassOfSvelteComponent;
* ```
* will throw a type error, so we need to seperate the more strictly typed class.
*/
export class SvelteComponentTyped<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> extends SvelteComponent<Props, Events> {
> extends SvelteComponentDev {
/**
* @private
* For type checking capabilities only.
Expand Down Expand Up @@ -142,24 +222,9 @@ export class SvelteComponentDev<
hydrate?: boolean;
intro?: boolean;
$$inline?: boolean;
}) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error("'target' is a required option");
}

super();
}) {
Copy link

@guidobouman guidobouman Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whitespace.

super(options);
}

$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn('Component was already destroyed'); // eslint-disable-line no-console
};
}

$capture_state() {}

$inject_state() {}
}

export function loop_guard(timeout) {
Expand Down