Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jomifepe/98e4622e060113e9580af4bdd2dd53c0 to your computer and use it in GitHub Desktop.
Save jomifepe/98e4622e060113e9580af4bdd2dd53c0 to your computer and use it in GitHub Desktop.
Article Snippet - Clean up your React component types 🧼
// types/util.ts
export type WithChildren<P = {}> = P & { children?: ReactNode };
// components/MyComponent.tsx
import { WithChildren } from 'types/util.ts';
const MyComponent1 = ({ children }: WithChildren) => <>{children}</>;
const MyComponent2 = ({ children }: WithChildren<MyProps>) => <>{children}</>;
type MyProps = { 
children?: React.ReactNode; 
  /* … my properties */ 
}
const MyComponent = ({ children }: MyProps) => <>{children}</>;
// Letting TypeScript inferring the type: (props: MyProps) => JSX.Element 🚀
const MyComponent = (props: MyProps) => <div>beep</div>;
// Explicitly defining a return type (3 similar options of writting the same as above)
const MyComponent = (props: MyProps): ReactElement => <div>boop</div>;
const MyComponent = (props: MyProps): JSX.Element => <div>boop</div>;
const MyComponent: FC<MyProps> = (props) => <div>boop</div>;
const List = (props: ListProps) => { /* JSX */ }
List.Item = (props: ItemProps) => { /* JSX */ }
// <List><List.Item></List>
const MyComponent = (props: MyProps) => { /* JSX */ }
MyComponent.displayName = 'MySpecialComponent';
// Inferred type:
// const MyComponent: {
// (props: MyProps): JSX.Element;
// displayName: string;
// }
// @types/react v16.9.35
// FC is an alias for FunctionComponent
type FC<P = {}> = FunctionComponent<P>;
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
type MyProps<T> = {
data: T;
onClick?: (item: T) => void;
}
// with a function declaration ✅
function MyComponent<T>(props: MyProps<T>) { /* JSX */ }
// with a function expression ✅
const MyComponent = <T>(props: MyProps<T>) => { /* JSX */ }
// We can pass anything to the data prop and it will infer the type
const MyComponent = ({ name = 'John' }: MyProp) => <div>{name}</div>
// if the name is undefined, uses 'John' as a fallback
type MyType = PropsWithChildren<{ name: string }>
 // result: { name: string; children?: ReactNode }
const MyComponent = ({ children }: PropsWithChildren<MyProps>) => (
<>{children}</>
)
type ListComponentType = FC<ListProps> & { Item: FC<ItemProps> };
const List: ListComponentType = (props) => { /* JSX */ }
List.Item = (props) => { /* JSX */ }
// <List><List.Item></List>
// These function expressions work fine ✅
const MyComponent: FC<MyProps> = (props) => { /* JSX */ };
const MyComponent: FC<MyProps> = function(props) { /* JSX */ }
const MyComponent = ((props) => { /* JSX */ }) as FC<MyProps>
// But function declarations don't ❌
// The following options have invalid syntax:
function MyComponent: FC<MyProps> (props) { /* JSX */ }
function <FC<MyProps>>MyComponent(props) { /* JSX */ }
function MyComponent(props) { /* JSX */ } as FC<MyProps>
// This won't work ❌
const MyComponent: FC</* ??? */> = <T>(props: MyProps<T>) => { /* JSX */ }
// This syntax is not valid ❌
const MyComponent: FC<MyProps<T>> = (props: MyProps<T>) => { /* JSX */ }
// Yay, the following code works ✅
// But we lost type inference ❌ Is it even worth it? 🤷🏻‍♂️
type MyComponentType<T = unknown> = FC<MyProps<T>>
const MyComponent: MyComponentType = (props) => { /* JSX */ }
const MyTypedComponent = MyComponent as MyComponentType<string[]>;
// Usage:
// <MyTypedComponent data={['beep', 'boop']} /> // ✅
// <MyTypedComponent data={[123, 456]} /> // ❌
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment