描述错误
当slot props
引用块作用域变量时,它在消费者中具有任何类型
再现
重现行为的步骤:
组件.svelte
<script>
let list = ['']
</script>
{#each list as value}
<slot props={value} />
{/each}
消费者
<Component let:name></Component>
其中name
的类型为 any
预期行为
有正确的类型
截图
如果适用,请添加屏幕截图以帮助解释您的问题。
系统(请填写以下信息):
附加上下文
得到转变为
<></>;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 }
声明变量的块可能必须返回 props 定义。 它必须通过多个块。 像这样:
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 }}}
但最大的问题是它需要向上移动,直到它没有嵌套在元素或组件中,以便我们可以事件声明任何变量。
我们还需要检查表达式是否引用了另一个插槽道具。 整个过程太乏味了,我觉得还是不支持比较好,提供一种方法让用户手动定义它的类型。
也许像
interface ComponentDef {
props?: ... // optional
slots?: ... // optional
}
这样你就可以明确地定义 props,同时定义 props 和 slot 之间的连接,就像#273 中想要的那样:
interface ComponentDef<T>{
props: { items: T[]; };
slots: { item: T };
}
svelte2tsx
将检查是否存在名为ComponentDef
的接口定义,如果存在,将使用它而不是构建自己的 props/slots 定义。
剩下的一件事是:如何在组件的其余部分中使用该泛型类型?
interface ComponentDef<T>{
props: { items: T[]; };
slots: { item: T };
}
export let items: T[]; // <<-- ??? should that be possible?
声明变量的块可能必须返回 props 定义。 它必须通过多个块。 像这样:
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 }}}
但最大的问题是它需要向上移动,直到它没有嵌套在元素或组件中,以便我们可以事件声明任何变量。
为什么允许Promise<T>
?
我不知道{#each promise as value}
是一回事。
那不行吗?
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) }
}
};
}
如果我将鼠标悬停在unwrap(list)
vscode 会显示string | number
。
@dummdidumm那么你必须指定道具类型两次? 🤔
我知道,但是如何定义它们之间的关系呢?
我们还可以保留接口名称ComponentProps
和ComponentSlots
,因此如果插槽和道具之间没有要建模的关系,您可以只定义ComponentSlots
- 没有重复。
我在想这样的事情:
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) }
}
};
}
但是,您必须使用传递给组件的事物的类型调用渲染函数。
我还没有找到它在哪里被调用。 它在苗条检查中吗? 还是因为 tsx 使渲染功能保持原样?
它被称为无处,这是问题的一部分。
编辑:也许它是通过 jsx 语法间接调用的,但我不知道具体是如何调用的。
我认为即使@PatrickG的解决方案更容易实现,我们仍然可以提供手动键入组件的方法。 它可以作为边缘情况、错误的回退,甚至可以手动输入事件。
interface ComponentEvent {
on(event: 'click', handler: (e: MouseEvent) => any): void
on(event: 'some-event', handler: (e: CustomEvent<number>) => void): void
}
如果我们采用接口定义解决方案,这是一个很大的变化。 其他人对此有意见吗?
至于帕特里克的解决方案,我认为我们需要在走 AST 时跟踪变量范围。 每当我们发现插槽道具引用块范围变量时,我们必须将变量和变量的初始值设定项添加到Map
。 然后在更高的范围内。 检查变量初始值设定项及其初始值设定项是否引用块作用域变量。
例如:
<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
每块key
,值是unwrap(keys)
items
每块item
,值是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
每块innerItem
,值是unwrap(innerItem)
items
每块item
,值是unwrap(items)
, unwrap(innerItem)
变成了unwrap(unwrap(items))
还有一些类型缩小的情况需要考虑:
<script lang="ts">
export let bla: string | null;
</script>
{#if bla !== null}
<slot value={bla}></slot> // <--- bla is now of type string
{/if}
---> 我认为这些情况对我们来说很难动态推断/需要做太多工作而收益太少。 我认为在这种情况下让用户明确输入插槽/道具会更好。
编辑:我突然想到:如果我们采用接口方法,我们如何确保接口正确实现? 因此,例如,如果有人输入interface ComponentSlots { slot1: number }
但输入<slot slot1={'a string'}></slot>
,我们如何捕获该类型错误?
我们不需要变量本身的 Map,只需要变量在<slot />
。
我发现https://www.typescriptlang.org/docs/handbook/jsx.html#attribute -type-checking - 也许这会有所帮助。
在浏览 Htmlx AST 时,没有关于变量类型的信息。 即使它是一个打字稿 AST,也没有类型信息,只有与它关联的类型节点。
一种疯狂的转换方式,这样我们就不必关心解构了
function render() {
let list = [];
<>
{(list).map(({ a }) => {
return <>
<slot props={a} />
</>;
})}
</>;
return {
props: {},
slots: {
default: { props: (({ a }) => a)(__sveltets_unwrapArr(list)) }
}
};
}
也许这真的可以以通用的方式工作:
#each
,请执行(__svelte_ts_unwrappAttr(list))
#await
,请执行相同操作,但使用__svelte_ts_unwrapPromiseLike(promise)
最有用的评论
也许这真的可以以通用的方式工作:
#each
,请执行(__svelte_ts_unwrappAttr(list))
#await
,请执行相同操作,但使用__svelte_ts_unwrapPromiseLike(promise)