Création de composants polymorphes dans TypeScript

Dans ce petit conseil, extrait de Libérer la puissance de TypeScriptSteve vous montre comment utiliser les composants polymorphes dans TypeScript.
Dans mon article Extension des propriétés d’un élément HTML dans TypeScriptje vous ai dit qu’au cours de la création d’une application volumineuse, j’ai tendance à finir par créer quelques wrappers autour des composants. Box
est un wrapper primitif autour des éléments de bloc de base en HTML (tels que <div>
, <aside>
, <section>
, <article>
, <main>
, <head>
, et ainsi de suite). Mais tout comme nous ne voulons pas perdre toute la signification sémantique que nous tirons de ces balises, nous n’avons pas non plus besoin de multiples variantes de Box
tout cela est fondamentalement pareil. Ce que nous aimerions faire, c’est utiliser Box
mais aussi pouvoir préciser ce qu’il doit y avoir sous le capot. UN composant polymorphe est un composant unique adaptable qui peut représenter différents éléments HTML sémantiques, TypeScript s’adaptant automatiquement à ces changements.
Voici une version trop simplifiée d’un Box
élément inspiré de Composants stylisés.
Et voici un exemple de Box
composant de Pâte, Celui de Twilio système de conception :
<Box as="article" backgroundColor="colorBackgroundBody" padding="space60">
Parent box on the hill side
<Box
backgroundColor="colorBackgroundSuccessWeakest"
display="inline-block"
padding="space40"
>
nested box 1 made out of ticky tacky
</Box>
</Box>
Voici une implémentation simple qui ne passe par aucun des accessoires, comme nous l’avons fait avec Button
et LabelledInputProps
au-dessus de:
import { PropsWithChildren } from 'react';
type BoxProps = PropsWithChildren<{
as: 'div' | 'section' | 'article' | 'p';
}>;
const Box = ({ as, children }: BoxProps) => {
const TagName = as || 'div';
return <TagName>{children}</TagName>;
};
export default Box;
Nous allons bien as
à TagName
, qui est un nom de composant valide dans JSX. Cela fonctionne en ce qui concerne React, mais nous voulons également que TypeScript s’adapte en conséquence à l’élément que nous définissons dans le as
soutenir:
import { ComponentProps } from 'react';
type BoxProps = ComponentProps<'div'> & {
as: 'div' | 'section' | 'article' | 'p';
};
const Box = ({ as, children }: BoxProps) => {
const TagName = as || 'div';
return <TagName>{children}</TagName>;
};
export default Box;
Honnêtement, je ne sais même pas si des éléments comme <section>
avoir des propriétés qu’un <div>
n’a pas. Même si je suis sûr de pouvoir le rechercher, aucun de nous n’est satisfait de cette mise en œuvre.
Mais qu’est-ce que c’est 'div'
être transmis là-dedans et comment ça marche ? Si nous regardons la définition du type pour ComponentPropsWithRef
nous voyons ce qui suit :
type ComponentPropsWithRef<T extends ElementType> = T extends new (
props: infer P,
) => Component<any, any>
? PropsWithoutRef<P> & RefAttributes<InstanceType<T>>
: PropsWithRef<ComponentProps<T>>;
Nous pouvons ignorer tous ces ternaires. Nous sommes intéressés par ElementType
tout de suite:
type BoxProps = ComponentPropsWithRef<'div'> & {
as: ElementType;
};
D’accord, c’est intéressant, mais et si nous voulions le type d’argument que nous donnons à ComponentProps
être le même que… as
?
Nous pourrait essayez quelque chose comme ceci :
import { ComponentProps, ElementType } from 'react';
type BoxProps<E extends ElementType> = Omit<ComponentProps<E>, 'as'> & {
as?: E;
};
const Box = <E extends ElementType="div">({ as, ...props }: BoxProps<E>) => {
const TagName = as || 'div';
return <TagName {...props} />;
};
export default Box;
Maintenant, un Box
Le composant s’adaptera à n’importe quel type d’élément que nous transmettons avec le as
soutenir.
Nous pouvons maintenant utiliser notre Box
composant partout où nous pourrions autrement utiliser un <div>
:
<Box as="section" className="flex place-content-between w-full">
<Button className="button" onClick={decrement}>
Decrement
</Button>
<Button onClick={reset}>Reset</Button>
<Button onClick={increment}>Increment</Button>
</Box>
Vous pouvez voir le résultat final sur le polymorphic
branche du dépôt GitHub pour ce tutoriel.
Cet article est extrait de Libérer la puissance de TypeScriptdisponible sur SitePoint Premium et chez les détaillants de livres électroniques.
Source link