0

我在 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 或任何。

4

1 回答 1

3

渲染器类型

react-markdown 包的类型非常松散。它将类型声明renderers为对象映射

{[nodeType: string]: ElementType}

其中键可以是任何string(不仅仅是有效的节点类型),并且值具有从ReactElementType类型导入的类型。 意味着你的渲染器可以是一个内置的元素标签名称,比如or ,或者一个函数组件或类组件,它采用props。ElementType"p""div"any

您可以将对象键入为

const customRenderers: {[nodeType: string]: ElementType} = { ...

键入道具

ElementType 对于在渲染函数中获得类型安全根本没有用。类型说props可以是任何东西。如果我们能知道props我们的渲染函数实际上将被调用,那就太好了。

我们paragraph被 propsnodechildren. 使用codeprops languagevalue和调用node元素childrenlanguage不幸的是,像和之类的自定义道具 value在任何地方都没有记录在 Typescript 中。您可以在 react-markdown 源代码的函数中看到它们被设置。getNodeProps每种节点类型都有不同的道具。

键入节点

propsnodechildren是我们实际可以获得有用的 Typescript 信息的地方。

react-markdown 类型显示anodeContent类型是从底层 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>
  );
};

代码沙盒链接

于 2021-03-14T22:26:34.217 回答