реагировать на тип уценки для средства визуализации абзаца?
Ни на 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
совершенно бесполезен для обеспечения безопасности типов внутри функции рендеринга. Тип говорит, что может быть что угодно. Было бы неплохо, если бы мы знали, с чем на самом деле будет вызвана наша функция рендеринга.
Нашему звонят с реквизитом и. А
code
element вызывается с реквизитами, и. Пользовательские реквизиты, такие как
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>