反应段落渲染器的降价类型?
react markdown type for paragraph renderer?
我在包裹 react.markdown 的 github 和 index.d.ts 上都找不到任何东西。它看起来像非常简单的示例,但整个 google 不包含任何示例。我为降价组件编写了一个自定义渲染器,但我无法弄清楚渲染器的类型
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
的类型作为对象 map
{[nodeType: string]: ElementType}
其中键可以是任何 string
(不仅仅是有效的节点类型)并且值具有从 React typings 导入的类型 ElementType
。 ElementType
表示您的渲染器可以是内置元素标签名称,如 "p"
或 "div"
或函数组件或采用 any
道具的 class 组件。
您可以将对象键入为
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 that the type for a node
is the Content
type imported from the underlying markdown parser package mdast. This Content
type 是所有单个 markdown 节点类型的联合。这些单独的类型都有一个不同的 type
属性,其中一个 string
文字与我们要在 renderers
对象上设置的键相匹配!
所以最后我们知道有效键的类型是Content["type"]
。我们还知道特定 K
键的 node
属性将是 Extract<Content, { type: K }>
,它为我们提供了与此 type
属性 相匹配的联合成员。
props
对象上的 children
属性只是一个典型的 React children 属性,但并非所有节点类型都有子节点。我们可以通过查看 node
的类型并查看它是否具有 children
属性 来知道我们的 props
是否包含 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
个节点,但只有 return 满足条件 node.children[0].type === "image"
时的任何内容。这意味着所有其他段落都将被删除!你需要确保你 总是 return 一些东西。
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>
);
};
我在包裹 react.markdown 的 github 和 index.d.ts 上都找不到任何东西。它看起来像非常简单的示例,但整个 google 不包含任何示例。我为降价组件编写了一个自定义渲染器,但我无法弄清楚渲染器的类型
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
的类型作为对象 map
{[nodeType: string]: ElementType}
其中键可以是任何 string
(不仅仅是有效的节点类型)并且值具有从 React typings 导入的类型 ElementType
。 ElementType
表示您的渲染器可以是内置元素标签名称,如 "p"
或 "div"
或函数组件或采用 any
道具的 class 组件。
您可以将对象键入为
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 that the type for a node
is the Content
type imported from the underlying markdown parser package mdast. This Content
type 是所有单个 markdown 节点类型的联合。这些单独的类型都有一个不同的 type
属性,其中一个 string
文字与我们要在 renderers
对象上设置的键相匹配!
所以最后我们知道有效键的类型是Content["type"]
。我们还知道特定 K
键的 node
属性将是 Extract<Content, { type: K }>
,它为我们提供了与此 type
属性 相匹配的联合成员。
props
对象上的 children
属性只是一个典型的 React children 属性,但并非所有节点类型都有子节点。我们可以通过查看 node
的类型并查看它是否具有 children
属性 来知道我们的 props
是否包含 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
个节点,但只有 return 满足条件 node.children[0].type === "image"
时的任何内容。这意味着所有其他段落都将被删除!你需要确保你 总是 return 一些东西。
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>
);
};