Bug报告
我们刚刚发布了Preact Signals,它是一种既可以用于Preact又可以用于React的附加组件,允许您将可观察对象直接传递到JSX中。作为其中的一部分,我们需要扩展现有的JSX定义,以允许我们也传递可观察类型,而不仅仅是现有的类型。但是我无法做到这一点。我一直收到一个类型错误。
🔎搜索词
JSX,声明合并,React,Preact,信号,IntrinsicElements
🕗版本和回归信息
- 这是我在每个版本中尝试的行为,我也查阅了关于JSX + 声明合并的常见问题解答条目
⏯ Playground链接
带有相关代码的Playground链接
💻代码
import * as React from "react"
// This and the JSX additions live in a library
interface Signal<T> {
value: T
}
declare global {
namespace JSX {
interface IntrinsicElements {
// Doesn't work
input:
| React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>,HTMLInputElement>
| Signal<string>
}
}
}
// This represents app code the user writes
const foo: Signal<string> = {
value: "foo"
}
const a = <input value={foo} />
🙁实际行为
将Signal
作为value
传递时出现类型错误
const foo = { value: "foo" };
const a = <input value={foo} />
🙂预期行为
将Signal
作为value
传递应该可以工作
const foo = { value: "foo" };
const a = <input value={foo} />
6条答案
按热度按时间6l7fqoea1#
请注意,您不必使用(并且我认为不应该)全局JSX命名空间。
您可以通过配置
jsxImportSource
来解决这个问题,这将带来自己的本地JSX命名空间,不会污染全局命名空间。我看到您已经在Preact中利用了本地JSX命名空间。我认为您应该将操纵后的JSXInternal
与来自您的@preact/signals
包的运行时工厂一起重新导出,人们应该在他们的tsconfig.json
中指向该包。wztqucjr2#
是的,在Preact中我们使用本地JSX命名空间。告诉用户指向另一个包的JSX是可以的,但我担心这种方法一旦有其他包也想扩展类型就会出问题。今天只有Signals,但我可以想象例如有一个RxJS绑定的原生适配器。所以我们需要一种方法来结合所有它们。
x0fgdtte3#
告诉用户指向另一个包的JSX将起作用,但我担心这种方法一旦有另一个包也想扩展类型就会失效。
这是一个有效的担忧,但仍然可能通过类型的组合来处理这个问题。请注意,这至少会在某个地方引用"组合"模块,并且它会使其可发现 - 而不是依赖于隐式扩展。它也不需要引入任何新的语言结构 - 目前这些接口的行为与通常的接口合并方式相同,但当然,这并不适合您的用例。这种行为不能改变 - 因此必须引入一些新的东西来解决这个问题。
总的来说,接口合并的问题在于它需要对项目有完整的了解才能正确工作 - 因此从本质上讲,它会降低项目的启动时间等。这只是我的两分钱观点,但显式依赖>>>隐式依赖,正是因为这个原因。
gjmwrych4#
我担心这种方法一旦有另一个包也想要扩展类型,就会失效。在一般情况下,如何使这种情况通用化,除了已经提出的上层方案之外,还有其他方法吗?如果
Signal2
也存在并且想要使事情正常工作,会发生什么?TS 应该使用 Union 还是 Intersection 来合并这两个类型?为什么/如何?这非常不清楚。即使我们有一些关键字,如
merge
,这样你就可以使用input: merge | Signal<string>
来引用你正在覆盖的属性,但你仍然会遇到声明顺序问题,因为在同一属性槽中重复出现的merge
可能以具有顺序依赖性的方式表现出来(例如input: Foo<merge>
)qqrboqgw5#
我还想更好地理解一件事,那就是Preact是如何让React起作用的。通常,“这里的类型应该做什么”的问题可以通过更精确地询问运行时如何工作来更容易地回答。
ff29svar6#
TS应该使用Union还是Intersection来合并这两种类型?为什么/如何?这非常不清楚。即使我们有一些关键字,如merge,以便你可以写入input: merge | Signal来引用你正在覆盖的属性,然后你会遇到声明顺序问题,因为在同一个属性槽中重复出现的merge可能会以具有顺序依赖性的方式表现出来(例如:input: Foo)
这是一个很好的观点,我最初创建这个问题时没有考虑到这一点。我同意你的观点,这里的歧义太多了,很容易陷入一个无底洞。听起来显式地组合消费代码库中的类型是正确的方法。
我还想更好地理解Preact是如何让React起作用的。通常,“这里应该是什么类型”的问题可以通过更精确地询问运行时是如何工作的来更容易回答的。
对于Preact来说,显然要容易得多,因为我们提供了官方的方法来接入渲染器。对于React来说,这更棘手,涉及到侵入内部。我认为从一个类型系统中推导出类型几乎是不可能的,因为它被一堆抽象所掩盖。