pull/17348/merge
That Richan 3 days ago committed by GitHub
commit 8ffa261de1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix too complex type error when using union props type by

@ -1,5 +1,4 @@
import type { SourceMap } from 'magic-string';
import type { Binding } from '../phases/scope.js';
import type { AST, Namespace } from './template.js';
import type { ICompileDiagnostic } from '../utils/compile_diagnostic.js';
import type { StateCreationRuneName } from '../../utils.js';

@ -117,6 +117,10 @@ type Branded<T, B> = T & Brand<B>;
*/
export type ComponentInternals = Branded<{}, 'ComponentInternals'>;
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
? I
: never;
/**
* Can be used to create strongly typed Svelte components.
*
@ -142,7 +146,7 @@ export type ComponentInternals = Branded<{}, 'ComponentInternals'>;
export interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = string
Bindings extends keyof UnionToIntersection<Required<Props>> | (string & {}) | '' = string
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.

@ -65,7 +65,7 @@ export function element(renderer, tag, attributes_fn = noop, children_fn = noop)
* Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
* @template {Record<string, any>} Props
* @param {Component<Props> | ComponentType<SvelteComponent<Props>>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} [options]
* @param {{ props?: Props; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} [options]
* @returns {RenderOutput}
*/
export function render(component, options = {}) {

@ -506,7 +506,7 @@ export class Renderer {
* Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
* @template {Record<string, any>} Props
* @param {Component<Props>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} [options]
* @param {{ props?: Props; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} [options]
* @returns {RenderOutput}
*/
static render(component, options = {}) {
@ -629,7 +629,7 @@ export class Renderer {
*
* @template {Record<string, any>} Props
* @param {Component<Props>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} options
* @param {{ props?: NoInfer<Props>; context?: Map<any, any>; idPrefix?: string }} options
* @returns {AccumulatedContent}
*/
static #render(component, options) {
@ -650,7 +650,7 @@ export class Renderer {
*
* @template {Record<string, any>} Props
* @param {Component<Props>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} options
* @param {{ props?: Props; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} options
* @returns {Promise<AccumulatedContent & { hashes: { script: Sha256Source[] } }>}
*/
static async #render_async(component, options) {
@ -760,7 +760,7 @@ export class Renderer {
* @template {Record<string, any>} Props
* @param {'sync' | 'async'} mode
* @param {import('svelte').Component<Props>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options
* @param {{ props?: Props; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options
* @returns {Renderer}
*/
static #open_render(mode, component, options) {

@ -13,7 +13,7 @@ export function render<
? [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options?: {
props?: Omit<Props, '$$slots' | '$$events'>;
props?: Props;
context?: Map<any, any>;
idPrefix?: string;
csp?: Csp;
@ -23,7 +23,7 @@ export function render<
: [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options: {
props: Omit<Props, '$$slots' | '$$events'>;
props: Props;
context?: Map<any, any>;
idPrefix?: string;
csp?: Csp;

@ -10,6 +10,7 @@ import {
type ComponentInternals
} from 'svelte';
import { render } from 'svelte/server';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from '../../elements';
SvelteComponent.element === HTMLElement;
@ -337,6 +338,154 @@ render(functionComponent, {
}
});
// --------------------------------------------------------------------------- union props
const unionComponent: Component<
{
binding: boolean;
} & (
| (HTMLButtonAttributes & {
readonly: string;
})
| (HTMLAnchorAttributes & {
readonly2: string;
})
),
{ foo: 'bar' },
'binding'
> = (a, props) => {
props.binding === true;
if ('readonly' in props) {
props.readonly === 'foo';
// @ts-expect-error
props.readonly = true;
}
if ('readonly2' in props) {
props.readonly2 === 'foo';
// @ts-expect-error
props.readonly2 = true;
}
// @ts-expect-error
props.binding = '';
return {
foo: 'bar'
};
};
unionComponent.element === HTMLElement;
const unionBindingIsOkayToWiden: Component<any> = unionComponent;
unionComponent(null as any, {
binding: true,
// @ts-expect-error
readonly: true
});
const unionComponentInstance = unionComponent(null as any, {
binding: true,
readonly: 'foo',
// @ts-expect-error
x: ''
});
unionComponentInstance.foo === 'bar';
// @ts-expect-error
unionComponentInstance.foo = 'foo';
const unionComponentProps: ComponentProps<typeof unionComponent> = {
binding: true,
readonly: 'foo',
type: 'button',
// @ts-expect-error
prop: 1
};
const unionComponentProps2: ComponentProps<typeof unionComponent> = {
binding: true,
readonly2: 'foo',
href: 'https://example.com',
// @ts-expect-error
prop: 1
};
// Test that self-typed functions are correctly inferred, too (use case: language tools has its own shape for backwards compatibility)
const unionComponentProps3: ComponentProps<(a: any, b: { a: true }) => { foo: string }> = {
a: true
};
mount(unionComponent, {
target: null as any as Document | Element | ShadowRoot,
props: {
binding: true,
readonly: 'foo',
type: 'button',
// @ts-expect-error
x: ''
}
});
mount(unionComponent, {
target: null as any as Document | Element | ShadowRoot,
props: {
binding: true,
// @ts-expect-error wrong type
readonly: 1
}
});
mount(
unionComponent,
// @ts-expect-error props missing
{ target: null as any }
);
// if component receives no args, props can be omitted
mount(null as any as Component<{}>, { target: null as any });
hydrate(unionComponent, {
target: null as any as Document | Element | ShadowRoot,
props: {
binding: true,
readonly: 'foo',
type: 'button',
// @ts-expect-error
x: ''
}
});
hydrate(unionComponent, {
target: null as any as Document | Element | ShadowRoot,
// @ts-expect-error missing prop
props: {
binding: true
}
});
hydrate(
unionComponent,
// @ts-expect-error props missing
{ target: null as any }
);
// if component receives no args, props can be omitted
hydrate(null as any as Component<{}>, { target: null as any });
render(unionComponent, {
props: {
binding: true,
readonly: 'foo',
type: 'button'
}
});
// @ts-expect-error
render(unionComponent);
render(unionComponent, {
// @ts-expect-error
props: {
binding: true
}
});
render(unionComponent, {
// @ts-expect-error
props: {
binding: true,
readonly: 1
}
});
// --------------------------------------------------------------------------- *.svelte components
// import from a nonexistent file to trigger the declare module '*.svelte' in ambient.d.ts

@ -116,6 +116,10 @@ declare module 'svelte' {
*/
export type ComponentInternals = Branded<{}, 'ComponentInternals'>;
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
? I
: never;
/**
* Can be used to create strongly typed Svelte components.
*
@ -141,7 +145,7 @@ declare module 'svelte' {
export interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = string
Bindings extends keyof UnionToIntersection<Required<Props>> | (string & {}) | '' = string
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.
@ -2210,7 +2214,7 @@ declare module 'svelte/motion' {
* const tween = Tween.of(() => number);
* </script>
* ```
*
*
*/
static of<U>(fn: () => U, options?: TweenOptions<U> | undefined): Tween<U>;
@ -2264,7 +2268,7 @@ declare module 'svelte/reactivity' {
* ```
*/
export class SvelteDate extends Date {
constructor(...params: any[]);
#private;
}
@ -2300,12 +2304,12 @@ declare module 'svelte/reactivity' {
* {#if monkeys.has('🙊')}<p>speak no evil</p>{/if}
* ```
*
*
*
*/
export class SvelteSet<T> extends Set<T> {
constructor(value?: Iterable<T> | null | undefined);
add(value: T): this;
#private;
}
@ -2351,12 +2355,12 @@ declare module 'svelte/reactivity' {
* {/if}
* ```
*
*
*
*/
export class SvelteMap<K, V> extends Map<K, V> {
constructor(value?: Iterable<readonly [K, V]> | null | undefined);
set(key: K, value: V): this;
#private;
}
@ -2419,7 +2423,7 @@ declare module 'svelte/reactivity' {
* ```
*/
export class SvelteURLSearchParams extends URLSearchParams {
[REPLACE](params: URLSearchParams): void;
#private;
}
@ -2495,7 +2499,7 @@ declare module 'svelte/reactivity' {
*/
export function createSubscriber(start: (update: () => void) => (() => void) | void): () => void;
class ReactiveValue<T> {
constructor(fn: () => T, onsubscribe: (update: () => void) => void);
get current(): T;
#private;
@ -2560,7 +2564,7 @@ declare module 'svelte/reactivity/window' {
get current(): number | undefined;
};
class ReactiveValue<T> {
constructor(fn: () => T, onsubscribe: (update: () => void) => void);
get current(): T;
#private;
@ -2583,7 +2587,7 @@ declare module 'svelte/server' {
? [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options?: {
props?: Omit<Props, '$$slots' | '$$events'>;
props?: Props;
context?: Map<any, any>;
idPrefix?: string;
csp?: Csp;
@ -2593,7 +2597,7 @@ declare module 'svelte/server' {
: [
component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp,
options: {
props: Omit<Props, '$$slots' | '$$events'>;
props: Props;
context?: Map<any, any>;
idPrefix?: string;
csp?: Csp;

Loading…
Cancel
Save