DEV Community

Ben Duncan
Ben Duncan

Posted on

React Typescript Cheatsheet

Here are a few patterns I find useful when building a project in React + Typescript. When starting out I didn't find the JSX and React types obvious or particularly well documented, so I'd like to maintain a list of what types to use in different situations.

This is just a start, so I'd love to know what patterns you all find useful and what I should add to the list. Also, if there is anything here that I shouldn't be doing I would love to know!

Table of Contents

Children

interface Props {
  children?: React.ReactNode
}
Enter fullscreen mode Exit fullscreen mode

Events

interface Props {
  // Use the specific HTML element targeted
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void
}
Enter fullscreen mode Exit fullscreen mode

Forwarding Props

To forward a named props object to an element:

interface Props {
  buttonProps?: JSX.IntrinsicElements['button']
}

const Container = ({ buttonProps }: Props) => (
  <div>
    <button {...buttonProps}></button>
  </div>
)
Enter fullscreen mode Exit fullscreen mode

To forward top-level props to an element:

interface Props extends JSX.IntrinsicElements['button'] {}
const ExtendedButton = (props: Props) => (
  <button {...props} />
)
Enter fullscreen mode Exit fullscreen mode

Styles

// utils.d.ts
declare interface StyleProps {
  style?: React.CSSProperties
  className?: string
}

// Button.tsx
interface ButtonProps extends StyleProps {
  label: string
}

const Button = ({ label, ...styleProps }: ButtonProps) => (
  <button {...styleProps}>{label}</button>
)
Enter fullscreen mode Exit fullscreen mode

Refs

Element refs:

const liRef = useRef<HTMLLIElement>(null)
Enter fullscreen mode Exit fullscreen mode

Other refs are possible too:

const valueRef = useRef<number>(0)
Enter fullscreen mode Exit fullscreen mode

Discussion (4)

Collapse
mixailnovikov profile image
Mikhail Novikov

Hello! Nice article, but I have some notes.

Children
To describe a 'children', I prefer to use React.FC generic type.

interface AnotherSpecificProps {}
const AnotherComponent: React.FC<AnotherSpecificProps> = ({ children }) => {};

// even with omitted props it's still working
const Component: React.FC = ({ children }) => {};

Enter fullscreen mode Exit fullscreen mode

Forwarding Props
Sometimes I need to pass additional props to the nested components, so there is a helpful generic React.ComponentProps<typeof SomeComponent>
Also you can wrap it in Partial<React.ComponentProps<typeof SomeComponent>>.

ComponentProps {
  prop: boolean;
  propAnother: string;
}
const Component: React.FC<ComponentProps> = ({prop, propAnother}) => {
  return (
    <div>
      {prop && propAnother}
    </div>
  );
};

interface ComplexComponentProps {
  componentProps?: Partial<React.ComponentProps<typeof Component>>
}
const ComplexComponent: React.FC<ComplexComponentProps> = ({componentProps}) => {
  return (
    <div>
      <Component prop={true} propAnother="string" {...componentProps} />
    </div>
  );
};

// somewhere else
<ComplexComponent />
<ComplexComponent componentProps={{prop: false}} />
// without Partial you should pass all required props, so Partial is very useful
Enter fullscreen mode Exit fullscreen mode
Collapse
bendman profile image
Ben Duncan Author

Thanks for these! The React.ComponentProps<typeof Component> looks especially useful for passing props down without needing to import the Props interface of the child component.

Collapse
aranoe profile image
aranoe

Hey man, nice article!

I only see one problem. For me the " forward top-level props to an element" section doesn't work.
Because when I do:

interface Props extends JSX.IntrinsicElements['button'] {}
Enter fullscreen mode Exit fullscreen mode

I get this error:

An interface can only extend an identifier/qualified-name with optional type arguments.ts(2499)
Enter fullscreen mode Exit fullscreen mode

However, there is an easy workaround to fix this problem:

type ButtonProps = JSX.IntrinsicElements['button'];
interface Props extends ButtonProps {}
Enter fullscreen mode Exit fullscreen mode

But this seems really combersome and repetitive when you have to do this in every component.
I wrote a generator which basically generates me this:

namespace JSXProps {
  type AElement = JSX.IntrinsicElements["a"];
  type AbbrElement = JSX.IntrinsicElements["abbr"];
  type AddressElement = JSX.IntrinsicElements["address"];
  type AreaElement = JSX.IntrinsicElements["area"];
  type ArticleElement = JSX.IntrinsicElements["article"];
  type AsideElement = JSX.IntrinsicElements["aside"];
  type AudioElement = JSX.IntrinsicElements["audio"];
  type BElement = JSX.IntrinsicElements["b"];
  type BaseElement = JSX.IntrinsicElements["base"];
  type BdiElement = JSX.IntrinsicElements["bdi"];
  type BdoElement = JSX.IntrinsicElements["bdo"];
  type BigElement = JSX.IntrinsicElements["big"];
  type BlockquoteElement = JSX.IntrinsicElements["blockquote"];
  type BodyElement = JSX.IntrinsicElements["body"];
  type BrElement = JSX.IntrinsicElements["br"];
  type ButtonElement = JSX.IntrinsicElements["button"];
  type CanvasElement = JSX.IntrinsicElements["canvas"];
  type CaptionElement = JSX.IntrinsicElements["caption"];
  type CiteElement = JSX.IntrinsicElements["cite"];
  type CodeElement = JSX.IntrinsicElements["code"];
  type ColElement = JSX.IntrinsicElements["col"];
  type ColgroupElement = JSX.IntrinsicElements["colgroup"];
  type DataElement = JSX.IntrinsicElements["data"];
  type DatalistElement = JSX.IntrinsicElements["datalist"];
  type DdElement = JSX.IntrinsicElements["dd"];
  type DelElement = JSX.IntrinsicElements["del"];
  type DetailsElement = JSX.IntrinsicElements["details"];
  type DfnElement = JSX.IntrinsicElements["dfn"];
  type DialogElement = JSX.IntrinsicElements["dialog"];
  type DivElement = JSX.IntrinsicElements["div"];
  type DlElement = JSX.IntrinsicElements["dl"];
  type DtElement = JSX.IntrinsicElements["dt"];
  type EmElement = JSX.IntrinsicElements["em"];
  type EmbedElement = JSX.IntrinsicElements["embed"];
  type FieldsetElement = JSX.IntrinsicElements["fieldset"];
  type FigcaptionElement = JSX.IntrinsicElements["figcaption"];
  type FigureElement = JSX.IntrinsicElements["figure"];
  type FooterElement = JSX.IntrinsicElements["footer"];
  type FormElement = JSX.IntrinsicElements["form"];
  type H1Element = JSX.IntrinsicElements["h1"];
  type H2Element = JSX.IntrinsicElements["h2"];
  type H3Element = JSX.IntrinsicElements["h3"];
  type H4Element = JSX.IntrinsicElements["h4"];
  type H5Element = JSX.IntrinsicElements["h5"];
  type H6Element = JSX.IntrinsicElements["h6"];
  type HeadElement = JSX.IntrinsicElements["head"];
  type HeaderElement = JSX.IntrinsicElements["header"];
  type HgroupElement = JSX.IntrinsicElements["hgroup"];
  type HrElement = JSX.IntrinsicElements["hr"];
  type HtmlElement = JSX.IntrinsicElements["html"];
  type IElement = JSX.IntrinsicElements["i"];
  type IframeElement = JSX.IntrinsicElements["iframe"];
  type ImgElement = JSX.IntrinsicElements["img"];
  type InputElement = JSX.IntrinsicElements["input"];
  type InsElement = JSX.IntrinsicElements["ins"];
  type KbdElement = JSX.IntrinsicElements["kbd"];
  type KeygenElement = JSX.IntrinsicElements["keygen"];
  type LabelElement = JSX.IntrinsicElements["label"];
  type LegendElement = JSX.IntrinsicElements["legend"];
  type LiElement = JSX.IntrinsicElements["li"];
  type LinkElement = JSX.IntrinsicElements["link"];
  type MainElement = JSX.IntrinsicElements["main"];
  type MapElement = JSX.IntrinsicElements["map"];
  type MarkElement = JSX.IntrinsicElements["mark"];
  type MenuElement = JSX.IntrinsicElements["menu"];
  type MenuitemElement = JSX.IntrinsicElements["menuitem"];
  type MetaElement = JSX.IntrinsicElements["meta"];
  type MeterElement = JSX.IntrinsicElements["meter"];
  type NavElement = JSX.IntrinsicElements["nav"];
  type NoindexElement = JSX.IntrinsicElements["noindex"];
  type NoscriptElement = JSX.IntrinsicElements["noscript"];
  type ObjectElement = JSX.IntrinsicElements["object"];
  type OlElement = JSX.IntrinsicElements["ol"];
  type OptgroupElement = JSX.IntrinsicElements["optgroup"];
  type OptionElement = JSX.IntrinsicElements["option"];
  type OutputElement = JSX.IntrinsicElements["output"];
  type PElement = JSX.IntrinsicElements["p"];
  type ParamElement = JSX.IntrinsicElements["param"];
  type PictureElement = JSX.IntrinsicElements["picture"];
  type PreElement = JSX.IntrinsicElements["pre"];
  type ProgressElement = JSX.IntrinsicElements["progress"];
  type QElement = JSX.IntrinsicElements["q"];
  type RpElement = JSX.IntrinsicElements["rp"];
  type RtElement = JSX.IntrinsicElements["rt"];
  type RubyElement = JSX.IntrinsicElements["ruby"];
  type SElement = JSX.IntrinsicElements["s"];
  type SampElement = JSX.IntrinsicElements["samp"];
  type ScriptElement = JSX.IntrinsicElements["script"];
  type SectionElement = JSX.IntrinsicElements["section"];
  type SelectElement = JSX.IntrinsicElements["select"];
  type SmallElement = JSX.IntrinsicElements["small"];
  type SourceElement = JSX.IntrinsicElements["source"];
  type SpanElement = JSX.IntrinsicElements["span"];
  type StrongElement = JSX.IntrinsicElements["strong"];
  type StyleElement = JSX.IntrinsicElements["style"];
  type SubElement = JSX.IntrinsicElements["sub"];
  type SummaryElement = JSX.IntrinsicElements["summary"];
  type SupElement = JSX.IntrinsicElements["sup"];
  type TableElement = JSX.IntrinsicElements["table"];
  type TemplateElement = JSX.IntrinsicElements["template"];
  type TbodyElement = JSX.IntrinsicElements["tbody"];
  type TdElement = JSX.IntrinsicElements["td"];
  type TextareaElement = JSX.IntrinsicElements["textarea"];
  type TfootElement = JSX.IntrinsicElements["tfoot"];
  type ThElement = JSX.IntrinsicElements["th"];
  type TheadElement = JSX.IntrinsicElements["thead"];
  type TimeElement = JSX.IntrinsicElements["time"];
  type TitleElement = JSX.IntrinsicElements["title"];
  type TrElement = JSX.IntrinsicElements["tr"];
  type TrackElement = JSX.IntrinsicElements["track"];
  type UElement = JSX.IntrinsicElements["u"];
  type UlElement = JSX.IntrinsicElements["ul"];
  type VarElement = JSX.IntrinsicElements["var"];
  type VideoElement = JSX.IntrinsicElements["video"];
  type WbrElement = JSX.IntrinsicElements["wbr"];
  type WebviewElement = JSX.IntrinsicElements["webview"];
  type SvgElement = JSX.IntrinsicElements["svg"];
  type AnimateElement = JSX.IntrinsicElements["animate"];
  type AnimateMotionElement = JSX.IntrinsicElements["animateMotion"];
  type AnimateTransformElement = JSX.IntrinsicElements["animateTransform"];
  type CircleElement = JSX.IntrinsicElements["circle"];
  type ClipPathElement = JSX.IntrinsicElements["clipPath"];
  type DefsElement = JSX.IntrinsicElements["defs"];
  type DescElement = JSX.IntrinsicElements["desc"];
  type EllipseElement = JSX.IntrinsicElements["ellipse"];
  type FeBlendElement = JSX.IntrinsicElements["feBlend"];
  type FeColorMatrixElement = JSX.IntrinsicElements["feColorMatrix"];
  type FeComponentTransferElement = JSX.IntrinsicElements["feComponentTransfer"];
  type FeCompositeElement = JSX.IntrinsicElements["feComposite"];
  type FeConvolveMatrixElement = JSX.IntrinsicElements["feConvolveMatrix"];
  type FeDiffuseLightingElement = JSX.IntrinsicElements["feDiffuseLighting"];
  type FeDisplacementMapElement = JSX.IntrinsicElements["feDisplacementMap"];
  type FeDistantLightElement = JSX.IntrinsicElements["feDistantLight"];
  type FeDropShadowElement = JSX.IntrinsicElements["feDropShadow"];
  type FeFloodElement = JSX.IntrinsicElements["feFlood"];
  type FeFuncAElement = JSX.IntrinsicElements["feFuncA"];
  type FeFuncBElement = JSX.IntrinsicElements["feFuncB"];
  type FeFuncGElement = JSX.IntrinsicElements["feFuncG"];
  type FeFuncRElement = JSX.IntrinsicElements["feFuncR"];
  type FeGaussianBlurElement = JSX.IntrinsicElements["feGaussianBlur"];
  type FeImageElement = JSX.IntrinsicElements["feImage"];
  type FeMergeElement = JSX.IntrinsicElements["feMerge"];
  type FeMergeNodeElement = JSX.IntrinsicElements["feMergeNode"];
  type FeMorphologyElement = JSX.IntrinsicElements["feMorphology"];
  type FeOffsetElement = JSX.IntrinsicElements["feOffset"];
  type FePointLightElement = JSX.IntrinsicElements["fePointLight"];
  type FeSpecularLightingElement = JSX.IntrinsicElements["feSpecularLighting"];
  type FeSpotLightElement = JSX.IntrinsicElements["feSpotLight"];
  type FeTileElement = JSX.IntrinsicElements["feTile"];
  type FeTurbulenceElement = JSX.IntrinsicElements["feTurbulence"];
  type FilterElement = JSX.IntrinsicElements["filter"];
  type ForeignObjectElement = JSX.IntrinsicElements["foreignObject"];
  type GElement = JSX.IntrinsicElements["g"];
  type ImageElement = JSX.IntrinsicElements["image"];
  type LineElement = JSX.IntrinsicElements["line"];
  type LinearGradientElement = JSX.IntrinsicElements["linearGradient"];
  type MarkerElement = JSX.IntrinsicElements["marker"];
  type MaskElement = JSX.IntrinsicElements["mask"];
  type MetadataElement = JSX.IntrinsicElements["metadata"];
  type MpathElement = JSX.IntrinsicElements["mpath"];
  type PathElement = JSX.IntrinsicElements["path"];
  type PatternElement = JSX.IntrinsicElements["pattern"];
  type PolygonElement = JSX.IntrinsicElements["polygon"];
  type PolylineElement = JSX.IntrinsicElements["polyline"];
  type RadialGradientElement = JSX.IntrinsicElements["radialGradient"];
  type RectElement = JSX.IntrinsicElements["rect"];
  type StopElement = JSX.IntrinsicElements["stop"];
  type SwitchElement = JSX.IntrinsicElements["switch"];
  type SymbolElement = JSX.IntrinsicElements["symbol"];
  type TextElement = JSX.IntrinsicElements["text"];
  type TextPathElement = JSX.IntrinsicElements["textPath"];
  type TspanElement = JSX.IntrinsicElements["tspan"];
  type UseElement = JSX.IntrinsicElements["use"];
  type ViewElement = JSX.IntrinsicElements["view"];
}
Enter fullscreen mode Exit fullscreen mode

I put this in a global.d.ts file so I don't even have to import it.
Now I can use it like this:

interface Props extends JSXProps.ButtonElement {}
Enter fullscreen mode Exit fullscreen mode

Am I missing something? Is there a better solution to this? I think react should provide an easier type system for the end user in the first place tho... It was extremely frustrasting to find the right types when I started react.

Collapse
redhoodjt1988 profile image
Jonathan Reeves

This is awesome. Thanks. As I use TypeScript and React more I will definitely reach out and let you know if you need to add anything.