реагировать на тип уценки для средства визуализации абзаца?

Ни на github, ни на index.d.ts пакета response.markdown ничего найти не смог. это выглядит как очень простой пример, но весь 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>
  );
};

какой тип абзаца? Я проверил, что это Renderer:

         type Renderer<T> = (props: T) => ElementType<T>

Я не мог понять, что передать как T. Я пробовал, HtmlParagraphElement или что-то еще.

2 ответа

Тип рендерера

Пакет response-markdown очень слабо типизирован. Он объявляет тип как карту объектов

      {[nodeType: string]: ElementType}

где ключи могут быть любыми (а не только допустимыми типами узлов), а значения имеют тип, импортированный из типов React . означает, что ваше средство визуализации может быть встроенным именем тега элемента, например "p" или же "div" или функциональный компонент или компонент класса, который принимает any реквизит.

Вы можете просто ввести свой объект как

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

Набор реквизита

ElementTypeсовершенно бесполезен для обеспечения безопасности типов внутри функции рендеринга. Тип говорит, что может быть что угодно. Было бы неплохо, если бы мы знали, с чем на самом деле будет вызвана наша функция рендеринга.

Нашему звонят с реквизитом и. А codeelement вызывается с реквизитами, и. Пользовательские реквизиты, такие как language и valueк сожалению, нигде не документированы в Typescript. Вы можете увидеть, как они устанавливаются в getNodePropsфункция исходного кода реакции-уценки. Для каждого типа узла существуют разные свойства.

Ввод узла

Свойства и - это то место, где мы действительно можем получить полезную информацию о Typescript.

Типы response-markdown показывают, что тип для a - это тип, импортированный из базового пакета анализатора markdown mdast. Этот <tcode id="4376250"></tcode>Тип - это объединение всех отдельных типов узлов уценки. Все эти отдельные типы обладают отличным свойством, которое string литерал, соответствующий ключу, который мы хотим установить в нашем renderers объект!

Итак, наконец, мы знаем, что тип действительных ключей Content["type"]. Мы также знаем, что опора для определенного K ключ будет Extract<Content, { type: K }> что дает нам член союза, который соответствует этому type свойство.

Опора объекта - это просто типичная дочерняя опора React, но не у всех типов узлов есть дочерние элементы. Мы можем знать, действительно ли наши props включить, посмотрев на тип для node и посмотреть, есть ли у него собственность.

      type NodeToProps<T> = {
  node: T;
  children: T extends { children: any } ? ReactNode : never;
};

(это соответствует полученному реквизиту, потому что children prop всегда установлен, но будет 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>
  );
};

Ссылка на тестовую среду кода

Просто измените «рендерер» на «компоненты»:

      <ReactMarkdown components={customRenderers}>{blog.content}</ReactMarkdown>
Другие вопросы по тегам