React TS Components

Posted on Thu, Mar 4, 2021
A cleaner API for React TS components

I hope this article shares some light on how to build better React components leveraging TypeScript. This post is an outcome of the efforts of building taggr , the privacy-focused AI gallery. While building taggr , I got deeper into TypeScript, and so far I am loving the added capabilities for annotating types and catching errors at compile time, instead off at runtime.

❌ Don't Do like this

// paragraph.tsx
export type Props = {
  text: string;
}const Paragraph = ({text}: Props) => <p>{text}</p>// title.tsx
// Title is now tightly coupled to paragraph.txs > Props
import {Props} from './paragraph' const Title = ({text}: Props) => <h1>{text}</h1>

✅ Better Way

// models.ts
export interface User {
 name: string,
 age: number;
};
// user-profile.tsx
import {User} from './models';
type Props = {
 user: User,
 date: string, // other props
};
const UserProfile = ({user, date}: Props) => ...
export default UserProfile;
// user-list.tsx
import {User} from './models';
type Props = {
 users: User[],
};
const UserList = ({users}: Props) => ...
export default UserList;

// type-helpers.ts
import * as React from "react";
export type GetComponentProps<T> = T extends
| React.ComponentType<infer P>
| React.Component<infer P>
? P
: never;

// title.tsx
type Color = "RED" | "BLUE" | "GREEN";
type Props = {
 title: string;
 color: Color;
};
const Title = ({ title, color }: Props) => (
  <h1 style={{ color }}>{title}</h1>
);
export default Title;

// title-wrapper.tsx
import Title from './title';
type Props = GetComponentProps<typeof Title> & {
 onClick: () => void;
};
const TitleWrapper = ({onClick, ...rest}: Props) => (
  <div onClick={onClick}>
    <Title {...rest} /> 
  </div>
);
export default TitleWrapper;
// index.ts
import TitleWrapper from 'title-wrapper';
// Full type safety and autocompletion! 🎉
const App = () =>  (
 <TitleWrapper
  title="Hello there"
  color="GREEN"
  onClick={() => window.alert("title pressed")} 
 />
);