渲染器类型
react-markdown 包的类型非常松散。它将类型声明renderers
为对象映射
{[nodeType: string]: ElementType}
其中键可以是任何string
(不仅仅是有效的节点类型),并且值具有从ReactElementType
类型导入的类型。 意味着你的渲染器可以是一个内置的元素标签名称,比如or ,或者一个函数组件或类组件,它采用props。ElementType
"p"
"div"
any
您可以将对象键入为
const customRenderers: {[nodeType: string]: ElementType} = { ...
键入道具
ElementType
对于在渲染函数中获得类型安全根本没有用。类型说props
可以是任何东西。如果我们能知道props
我们的渲染函数实际上将被调用,那就太好了。
我们paragraph
被 propsnode
和children
. 使用code
props language
、value
和调用node
元素children
。language
不幸的是,像和之类的自定义道具 value
在任何地方都没有记录在 Typescript 中。您可以在 react-markdown 源代码的函数中看到它们被设置。getNodeProps
每种节点类型都有不同的道具。
键入节点
propsnode
和children
是我们实际可以获得有用的 Typescript 信息的地方。
react-markdown 类型显示anode
的Content
类型是从底层 markdown 解析器包 mdast 导入的类型。 此Content
类型是所有单个降价节点类型的联合。这些单独的类型都有一个独特的type
属性,该属性string
与我们要在renderers
对象上设置的键相匹配!
所以最后我们知道有效键的类型是Content["type"]
. 我们还知道,node
特定K
键的 propExtract<Content, { type: K }>
将为我们提供与该type
属性匹配的联合成员。
对象上的children
propprops
只是一个典型的 React children prop,但并非所有节点类型都有孩子。我们可以通过查看类型并查看它是否具有属性来知道我们是否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>
);
};
代码沙盒链接