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

Top comments (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

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.