如何强制孩子拥有某种属性

lgq*_*lgq 6 typescript reactjs

正如标题所说,我有一个像这样的反应FC:

const Test = (props: { 
    children: React.ReactElement<{ slot: "content" }> 
}) => {
    return <></>
}
Run Code Online (Sandbox Code Playgroud)

现在,传递一个没有 slot 属性的子元素,没有任何 ts 错误

const test = (
    <Test>
        <span>some content</span>
    </Test>
)
Run Code Online (Sandbox Code Playgroud)

我知道 span 标签将被推断为 JSX.Element,但是有什么方法可以让我收到 ts 错误,指出 span 没有 slot 属性

kca*_*kca 0

不,这是不可能的:
“JSX 结果类型”:

默认情况下,JSX 表达式的结果类型为any. 您可以通过指定接口来自定义类型JSX.Element。但是,无法从此接口检索有关 JSX 的元素、属性或子级的类型信息。它是一个黑匣子。

JSX 代码(如<span>some content</span>)始终被解释为类型JSX.Element(如您所说)。

JSX.Element定义为:

declare global {
    namespace JSX {
        interface Element extends React.ReactElement<any, any> {}
    }
}
Run Code Online (Sandbox Code Playgroud)

any根据定义可分配给任何类型(并且任何类型都可以分配给它)。

因此,没有办法限制组件内部的任何内容,因为没有关于传入内容的信息,它只是any.

为什么会出现问题呢?

您不必限制传入的类型,而是必须JSX.Element以某种方式覆盖创建的类型(但这也不起作用):

declare global {
    namespace JSX {
        interface Element<K, V> extends React.ReactElement<K, V> {} // <-- doesn't work
    }
}
Run Code Online (Sandbox Code Playgroud)

...所以K并且V是从 JSX 代码推断出来的。

但 TypeScript 无法从 JSX 代码推断类型。

TS 本身甚至不知道 JSX 代码应该编译成什么,您必须配置 TS,以便它编译以创建 React 元素(通常已经预先定义)。但是您也可以将 JSX 代码编译为完全不同的代码,或者根本不编译(即,仅将 JSX 代码不变地传递)。(例如 tsconfig.json 选项jsx或jsxFactory)

此外,TS 扫描源代码并应用/推断所有类型,然后创建 JS 代码(不含类型)并将 JSX 转换为 JS。

现在,我认为开发一个从 JSX 代码推断类型的系统在技术上是可能的,但这需要付出相当大的努力。例如:

  • 内部转换分2步,首先在类型检查之前将JSX转换为TS,然后将TS转换为JS
  • 定义语法,让用户灵活定义 JSX 代码如何映射到函数定义(甚至其他 JS 代码)
  • 用户或 TS 必须确保 JSX to JS 函数与 JSX to JS 类型定义匹配。例如,如何定义 JSX 属性key和children是特殊的?