没有 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
