问题描述
我在 github 和 react.markdown 包的 index.d.ts 上都找不到任何东西。它看起来像一个非常简单的例子,但整个谷歌不包含任何例子。我为 markdown 组件编写了一个自定义渲染器,但我无法弄清楚渲染器的类型
import ReactMarkdown,{ Renderers,Renderer,NodeType } from "react-markdown";
const PostContent: React.FC<PostContent> = ({ blog }) => {
const customrenderers: Renderers = {
paragraph(paragraph) {
const { node } = paragraph;
if (node.children[0].type === "image") {
const image = node.children[0];
return (
<div style={{ width: "100%",maxWidth: "60rem" }}>
<Image
src={`/images/posts/${blog.slug}/${image.src}`}
alt={image.alt}
width={600}
height={300}
/>
</div>
);
}
},};
return (
<article className="">
<ReactMarkdown renderers={customrenderers}>{blog.content}</ReactMarkdown>
</article>
);
};
段落的类型是什么?我检查了它是渲染器:
type Renderer<T> = (props: T) => ElementType<T>
我不知道要传递什么作为 T。我试过了,HtmlParagraphElement 或任何。
解决方法
渲染器类型
react-markdown 包的类型非常松散。它declares renderers
的类型作为对象映射
{[nodeType: string]: ElementType}
其中键可以是任何 string
(不仅仅是有效的节点类型)并且值具有从 React typings 导入的类型 ElementType
。 ElementType
表示您的渲染器可以是内置元素标记名称,例如 "p"
或 "div"
,也可以是采用 any
道具的函数组件或类组件。
您可以将对象键入为
const customRenderers: {[nodeType: string]: ElementType} = { ...
输入道具
ElementType
对于在渲染函数中获得类型安全根本没有用。类型表示 props
可以是任何东西。如果我们能知道我们的渲染函数实际上将用什么 props
调用,那就太好了。
我们的 paragraph
使用道具 node
和 children
被调用。 code
元素通过道具 language
、value
、node
和 children
被调用。不幸的是,像 language
和 value
这样的自定义道具没有记录在 Typescript 的任何地方。您可以在 react-markdown 源代码的 getNodeProps
函数中see them being set。每种节点类型都有不同的道具。
输入节点
道具 node
和 children
是我们实际上可以获取有用的 Typescript 信息的地方。
react-markdown 类型 show,node
的类型是从底层 Markdown 解析器包 mdast 导入的 Content
类型。 This Content
type 是所有单个降价节点类型的联合。这些单独的类型都有一个不同的 type
属性,它的 string
文字与我们要在 renderers
对象上设置的键相匹配!
所以最后我们知道有效键的类型是 Content["type"]
。我们还知道特定 node
键的 K
道具将为 Extract<Content,{ type: K }>
,它为我们提供与此 type
属性匹配的联合成员。
children
对象上的 props
道具只是典型的 React 子道具,但并非所有节点类型都有子节点。我们可以通过查看 props
的类型并查看它是否具有 children
属性来知道我们的 node
是否包含 children
。
type NodeToProps<T> = {
node: T;
children: T extends { children: any } ? ReactNode : never;
};
(这与接收到的道具匹配,因为 children
道具总是被设置,但如果不支持孩子,将是 undefined
)
所以现在我们可以为您的 customRenderers
-- 或任何自定义渲染器映射定义严格类型:
type CustomRenderers = {
[K in Content["type"]]?: (
props: NodeToProps<Extract<Content,{ type: K }>>
) => ReactElement;
};
条件覆盖
您的代码将拦截所有 paragraph
节点,但只会在满足条件 node.children[0].type === "image"
时返回任何内容。这意味着所有其他段落都被删除了!您需要确保总是返回一些东西。
const PostContent: React.FC<PostContent> = ({ blog }) => {
const customRenderers: CustomRenderers = {
// node has type mdast.Paragraph,children is React.ReactNode
paragraph: ({ node,children }) => {
if (node.children[0].type === "image") {
const image = node.children[0]; // type mdast.Image
return (
<div
style={{
width: "100%",maxWidth: "60rem"
}}
>
<img
src={`/images/posts/${blog.slug}/${image.src}`}
alt={image.alt}
width={600}
height={300}
/>
</div>
);
}
// return a standard paragraph in all other cases
else return <p>{children}</p>;
},};
return (
<article className="">
<ReactMarkdown renderers={customRenderers}>{blog.content}</ReactMarkdown>
</article>
);
};