Beschreibe den Fehler
Wenn slot props
auf eine Blockbereichsvariable verweist, hat sie einen beliebigen Typ im Consumer
Fortpflanzen
Schritte zum Reproduzieren des Verhaltens:
Komponente.schlank
<script>
let list = ['']
</script>
{#each list as value}
<slot props={value} />
{/each}
Verbraucher
<Component let:name></Component>
wobei name
einen Typ von any hat
Erwartetes Verhalten
den richtigen Typ haben
Screenshots
Fügen Sie gegebenenfalls Screenshots hinzu, um Ihr Problem zu erklären.
System (bitte füllen Sie die folgenden Informationen aus):
Zusätzlicher Kontext
habe mich verwandeln zu
<></>;function render() {
let list = [''];
; <>
{(list).map((value) => <>
<slot props={value} />
</>)}</> return { props: {}, slots: {default: {props:value}} }} export default class { $$prop_def = __sveltets_partial(render().props) $$slot_def = render().slots }
Die Blöcke, in denen die Variable deklariert ist, müssen wahrscheinlich die Props-Definition zurückgeben. Und es muss durch mehrere Blöcke gehen. so was:
declare function unwrap<T>(value: Promise<T>| T[]): T
const __$$_default_0_prop = unwrap((list).map((value) =>{ <>
<slot props={value} />
</>; return { props: value }}));<></>; return { props: {}, slots: { default: { ...__$$_default_0_prop }}}
Aber das größte Problem ist, dass es nach oben verschoben werden muss, bis es nicht in einem Element oder einer Komponente verschachtelt ist, damit wir jede Variable ereignisdeklarieren können.
Und wir müssen auch prüfen, ob der Ausdruck auf andere Slot-Requisiten verweist. Der ganze Prozess ist so mühsam, dass ich denke, es ist besser, ihn einfach nicht zu unterstützen und dem Benutzer eine Möglichkeit zu geben, seinen Typ manuell zu definieren.
Vielleicht sowas wie
interface ComponentDef {
props?: ... // optional
slots?: ... // optional
}
Auf diese Weise könnten Sie Requisiten explizit definieren und gleichzeitig Verbindungen zwischen Requisiten und Slots definieren, wie in #273 gewünscht:
interface ComponentDef<T>{
props: { items: T[]; };
slots: { item: T };
}
svelte2tsx
würde prüfen, ob eine Interface-Definition namens ComponentDef
existiert und wenn ja, würde diese verwenden, anstatt eine eigene Props/Slots-Definition zu konstruieren.
Bleibt noch eine Sache: Wie wird dieser generische Typ im Rest der Komponente verwendet?
interface ComponentDef<T>{
props: { items: T[]; };
slots: { item: T };
}
export let items: T[]; // <<-- ??? should that be possible?
Die Blöcke, in denen die Variable deklariert ist, müssen wahrscheinlich die Props-Definition zurückgeben. Und es muss durch mehrere Blöcke gehen. so was:
declare function unwrap<T>(value: Promise<T>| T[]): T const __$$_default_0_prop = unwrap((list).map((value) =>{ <> <slot props={value} /> </>; return { props: value }}));<></>; return { props: {}, slots: { default: { ...__$$_default_0_prop }}}
Aber das größte Problem ist, dass es nach oben verschoben werden muss, bis es nicht in einem Element oder einer Komponente verschachtelt ist, damit wir jede Variable ereignisdeklarieren können.
Warum Promise<T>
zulassen?
Ich bin mir nicht bewusst, dass {#each promise as value}
eine Sache ist.
Würde das nicht funktionieren?
declare function unwrap<T>(value: T[]): T
function render() {
let list = ['', 123];
<>
{(list).map((item) => {
return <>
<slot item={item} />
</>;
})}
</>;
return {
props: {},
slots: {
default: { item: unwrap(list) }
}
};
}
Wenn ich unwrap(list)
Maus über string | number
.
@dummdidumm dann müssen Sie die Requisitentypen zweimal angeben? 🤔
Ich weiß, aber wie soll man die Beziehung zwischen ihnen sonst definieren?
Wir könnten auch die Schnittstellennamen ComponentProps
und ComponentSlots
reservieren. Wenn also keine Beziehung zwischen Slots und Requisiten modelliert werden soll, können Sie einfach ComponentSlots
- keine Duplikate.
Ich dachte an sowas:
declare function unwrap<T>(value: T[]): T
function render<T>() {
let list: T[];
<>
{(list).map((item) => {
return <>
<slot item={item} />
</>;
})}
</>;
return {
props: {},
slots: {
default: { item: unwrap(list) }
}
};
}
Aber dann müssten Sie die Render-Funktion mit dem Typ aus dem Ding aufrufen, das Sie an die Komponente übergeben.
Ich habe noch nicht gefunden, wo das heißt. Ist es in Svelte-Check? Oder muss die Renderfunktion wegen tsx so bleiben wie sie ist?
Es heißt nirgendwo und das ist ein Teil des Problems.
Bearbeiten: Vielleicht wird es indirekt über die jsx-Syntax aufgerufen, aber ich weiß nicht genau, wie.
Ich denke, wir können immer noch die Möglichkeit bieten, Komponenten manuell einzugeben , auch wenn die Lösung von
interface ComponentEvent {
on(event: 'click', handler: (e: MouseEvent) => any): void
on(event: 'some-event', handler: (e: CustomEvent<number>) => void): void
}
Wenn wir uns für die Schnittstellendefinitionslösung entscheiden, ist dies eine ziemliche Veränderung. Haben andere Meinungen dazu?
Was Patricks Lösung angeht, denke ich, dass wir den variablen Umfang beim Gehen des AST verfolgen müssen. Immer wenn wir die Slot-Requisiten gefunden haben, die auf eine Blockbereichsvariable verweisen, müssen wir die Variable und den Initialisierer der Variablen zu einem Map
hinzufügen. Und dann in einem oberen Bereich. Überprüfen Sie, ob der Variableninitialisierer und ihre Initialisierer auf eine Blockbereichsvariable verweisen.
beispielsweise:
<script>
export let keys = [];
export let items = [];
</script>
<table>
{#each items as item}
<tr>
{#each keys as key}
<slot value={item[key]}></slot>
{/each}
</tr>
{/each}
</table>
keys
jeder Blockkey
und der Wert wäre unwrap(keys)
items
jeder Blockitem
und der Wert wäre unwrap(items)
<script>
export let items = [ [ ] ];
</script>
<table>
{#each items as item}
<tr>
{#each item as innerItem}
<slot value={innerItem}></slot>
{/each}
</tr>
{/each}
</table>
item
jeder BlockinnerItem
und der Wert wäre unwrap(innerItem)
items
jeder Blockitem
und der Wert wäre unwrap(items)
, aus unwrap(innerItem)
wurde unwrap(unwrap(items))
Es gibt auch Fälle von Typeinschränkungen, die berücksichtigt werden müssen:
<script lang="ts">
export let bla: string | null;
</script>
{#if bla !== null}
<slot value={bla}></slot> // <--- bla is now of type string
{/if}
---> Ich denke, diese Fälle sind für uns einfach zu schwer dynamisch abzuleiten / es kostet zu viel Arbeit für zu wenig Nutzen. Ich denke, es ist besser, den Benutzer in diesem Fall explizit Slots / Requisiten eingeben zu lassen.
Edit: Ist mir gerade in den Sinn gekommen: Wenn wir den Interface-Ansatz verfolgen, wie können wir dann sicherstellen, dass die Schnittstelle korrekt implementiert ist? Wenn also beispielsweise jemand interface ComponentSlots { slot1: number }
eingibt, aber <slot slot1={'a string'}></slot>
tut, wie fangen wir dann diesen Tippfehler ab?
Wir brauchen keine Zuordnung der Variablen selbst, sondern nur den Typ der Variablen zu dem Zeitpunkt, zu dem sie in <slot />
.
Ich habe festgestellt, dass https://www.typescriptlang.org/docs/handbook/jsx.html#attribute -type-checking - vielleicht könnte das hilfreich sein.
Beim Durchlaufen von Htmlx AST gibt es keine Informationen über den Typ einer Variablen. Selbst wenn es sich um ein Typoskript-AST handelt, gibt es keine Typinformationen, sondern nur den Typknoten, der ihm zugeordnet ist.
Eine verrückte Art der Transformation, damit wir uns nicht um Destrukturierung kümmern müssen
function render() {
let list = [];
<>
{(list).map(({ a }) => {
return <>
<slot props={a} />
</>;
})}
</>;
return {
props: {},
slots: {
default: { props: (({ a }) => a)(__sveltets_unwrapArr(list)) }
}
};
}
Vielleicht könnte das wirklich generisch funktionieren:
#each
eingeben, tun Sie (__svelte_ts_unwrappAttr(list))
#await
eingeben, machen Sie dasselbe, aber mit __svelte_ts_unwrapPromiseLike(promise)
Hilfreichster Kommentar
Vielleicht könnte das wirklich generisch funktionieren:
#each
eingeben, tun Sie(__svelte_ts_unwrappAttr(list))
#await
eingeben, machen Sie dasselbe, aber mit__svelte_ts_unwrapPromiseLike(promise)