网站搜索

没有 React 的 Typescript 和 JSX:子类型


所以我觉得我好像漏掉了什么显而易见的东西。我已经阅读了关于非 React 环境下 JSX 的文档,但我还是看不出我哪里做错了。请考虑以下代码:

/** @jsx pragma */

function pragma(node: any, props: any, ...children: any[]) {
  return node;
}

declare namespace JSX {
  type Element = string;
  interface ElementChildrenAttribute {
    children: {}; // specify children name to use
  }
}

interface PropsType {
  children: string;
  name: string;
}

export const Prop = (props: PropsType) => {};

const foo = <Prop name="foo">hello</Prop>;

我得到的错误(在最后一行)是:

Property 'children' is missing in type '{ name: string; }' but required in type 'PropsType'.

我可以用以下方式“纠正”这个:

const foo = <Prop name="foo" children="bye">hello</Prop>;

但我原本以为 hello 会被分配给 children。我不应该显式地指定 children 属性,对吧?我到底漏掉了什么?我原本以为原来的写法没问题(不会生成错误),因为在这种情况下 children 字段应该会被填充为字符串值 "hello"(并且不需要我添加 children 属性?!?)。

我这里漏了什么?是否有某种我需要在这里填写的 JSX 命名空间的其他类型/字段,才能让它工作?

澄清:我在这里关心的是 TypeScript 没有将字符串 “hello ” 分配给 children。如果它这样做,一切都会好。因此,这似乎不是我的 prop 类型的问题,而是我在重新声明 JSX 命名空间时遗漏了一些信息,告诉编译器如何处理我的“自定义” JSX。

另外,确实我在 props 中不需要包含 children。但我当然可以包含,而且我确实想包含,因为这允许我缩小类型。这完全是允许的(请参见此处的第二段),正是出于这个原因。在这里添加它不会创建一个新的 prop,它只是精确化了在 JSX.ElementChildrenAttribute 中定义的内容。

答案#1

在 React 中,你可以使用类型 PropsWithChildren<T>。这是 React 使用的类型,但你可以自己创建,并用 JSX 替换 React 的特定内容。

这应该能处理所有儿童可能的使用情况。

type Children = JSX.Element[] | JSX.Element | string | number | boolean | null | undefined;

type PropsWithChildren<P> = P & {
    children?: Children;
}

type PropsType = {
    name: string;
}

export const Bar = ({ name, children }: PropsWithChildren<PropsType>) => (
    <div>
        <p>{name}</p>
        <div>{children}</div>
    </div>
);

我测试了,这些都不会抛出类型错误。

export const ElementArray = () => (
    <Bar name="Foo">
        <div>Hello</div>
        <div>World</div>
    </Bar>
)

export const Element = () => (
    <Bar name="Foo">
        <div>Hello World</div>
    </Bar>
)

export const String = () => (
    <Bar name="Foo">Hello World</Bar>
)

export const Number = () => (
    <Bar name="Foo">{43110}</Bar>
)

export const Boolean = () => (
    <Bar name="Foo">{true}</Bar>
)

export const Null = () => (
    <Bar name="Foo">{null}</Bar>
)

export const Undefined = () => (
    <Bar name="Foo" />
)

此外,你可以使用 PropsWithChildren 创建你自己的 FC(函数式组件)类型,并像在 React 中那样使用它。

type FC<P = {}> = {
    (props: PropsWithChildren<P>): JSX.Element;
}

// usage
export const FooBar: FC<PropsType> = ({ name, children }) => (
    <div>
        <p>{name}</p>
        <p>{children}</p>
    </div>
);

答案#2

你在原来的代码 declare global 中有小的遗漏。以下代码可以正常运行且没有任何错误:

/** @jsx pragma */

function pragma(node: any, props: any, ...children: any[]) {
  return node;
}

declare global {
  namespace JSX {
    type Element = string;
    interface ElementChildrenAttribute {
      children: {}; // specify children name to use
    }
  }
}

interface PropsType {
  children: string;
  name: string;
}

export const Prop = (props: PropsType) => { return null };

const foo = <Prop name="foo">hello</Prop>; // children assumed

相关文章