Language-tools: Slot-Requisiten-Typ, wenn Referenzblockbereichsvariable

Erstellt am 2. Juli 2020  ·  16Kommentare  ·  Quelle: sveltejs/language-tools

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):

  • Betriebssystem: Windows
  • IDE: VSCode
  • Plugin/Paket: Svelte für VSCode, svelte2tsx

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 }
Fixed bug

Hilfreichster Kommentar

Vielleicht könnte das wirklich generisch funktionieren:

  • Wenn Sie #each eingeben, tun Sie (__svelte_ts_unwrappAttr(list))
  • Wenn Sie #await eingeben, machen Sie dasselbe, aber mit __svelte_ts_unwrapPromiseLike(promise)
  • Wenn sie Destrukturierung verwenden, umhüllen Sie sie mit dieser sofort aufgerufenen Funktion @jasonyu123 vorgeschlagen123
  • Tun Sie dies bei Bedarf rekursiv

Alle 16 Kommentare

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>
  1. keys jeder Block
    der Schlüssel wäre key und der Wert wäre unwrap(keys)
  2. items jeder Block
    der Schlüssel wäre item 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>
  1. item jeder Block
    der Schlüssel wäre innerItem und der Wert wäre unwrap(innerItem)
  2. items jeder Block
    der Schlüssel wäre item 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:

  • Wenn Sie #each eingeben, tun Sie (__svelte_ts_unwrappAttr(list))
  • Wenn Sie #await eingeben, machen Sie dasselbe, aber mit __svelte_ts_unwrapPromiseLike(promise)
  • Wenn sie Destrukturierung verwenden, umhüllen Sie sie mit dieser sofort aufgerufenen Funktion @jasonyu123 vorgeschlagen123
  • Tun Sie dies bei Bedarf rekursiv
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen