渲染器类型
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. 使用codeprops 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属性匹配的联合成员。
对象上的childrenpropprops只是一个典型的 React children prop,但并非所有节点类型都有孩子。我们可以通过查看类型并查看它是否具有属性来知道我们是否props包含。childrennodechildren
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>
);
};
代码沙盒链接