Basic Prop Type Examples
A list of TypeScript types you will use in a React + TypeScript app:
type AppProps = { message: string; count: number; disabled: boolean; /** array of a type! */ names: string[]; phonenums: number[]; /** string literals to specify exact string values, with a union type to join them together */ status: "waiting" | "success"; /** an object with known properties (but could have more at runtime) */ obj: { id: string; title: string; }; /** array of objects! (common) */ objArr: { id: string; title: string; }[]; /** any non-primitive value - can't access any properties (NOT COMMON but useful as a placeholder) */ obj2: object; /** an interface with no required properties - (NOT COMMON, except for things like `React.Component<{}, State>`) */ obj3: {}; /** a dict object with any number of properties of the same type */ dict1: { [key: string]: MyTypeHere; }; dict2: Record<string, MyTypeHere>; // equivalent to dict1 /** function that doesn't take or return anything (VERY COMMON) */ onClick: () => void; /** function with named prop (VERY COMMON) */ onChange: (id: number) => void; /** function type syntax that takes an event (VERY COMMON) */ onChange: (event: React.ChangeEvent<HTMLInputElement>) => void; /** alternative function type syntax that takes an event (VERY COMMON) */ onClick(event: React.MouseEvent<HTMLButtonElement>): void; /** any function as long as you don't invoke it (not recommended) */ onSomething: Function; /** an optional prop (VERY COMMON!) */ optional?: OptionalType; /** when passing down the state setter function returned by `useState` to a child component. `number` is an example, swap out with whatever the type of your state */ setState: React.Dispatch<React.SetStateAction<number>>; };
Useful React Prop Type Examples
export declare interface AppProps { children?: React.ReactNode; // best, accepts everything React can render childrenElement: JSX.Element; // A single React element style?: React.CSSProperties; // to pass through style props onChange?: React.FormEventHandler<HTMLInputElement>; // form events! the generic parameter is the type of event.target props: Props & React.ComponentPropsWithoutRef<"button">; // to impersonate all the props of a button element and explicitly not forwarding its ref props2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>; // to impersonate all the props of MyButtonForwardedRef and explicitly forwarding its ref }
Helpful tips:
- always use
interfacefor public API's definition when authoring a library or 3rd party ambient type definitions
Q. Why?
→ Because this allows a consumer to extend them via declaration merging, if some definitions are missing.
- consider using
typefor your React Component Props and State, for consistency and because it is more constrained.
Types vs interfaces:
✅ YUP
🚫 Nope
⚠️ In some cases
Aspect | Type | Interface |
Can describe functions | ✅ | ✅ |
Can describe constructors | ✅ | ✅ |
Can describe tuples | ✅ | ✅ |
Interfaces can extend it | ⚠️ | ✅ |
Classes can extend it | 🚫 | ✅ |
Classes can implement it ( implements) | ⚠️ | ✅ |
Can intersect another one of its kind | ✅ | ⚠️ |
Can create a union with another one of its kind | ✅ | 🚫 |
Can be used to create mapped types | ✅ | 🚫 |
Can be mapped over with mapped types | ✅ | ✅ |
Expands in error messages and logs | ✅ | 🚫 |
Can be augmented | 🚫 | ✅ |
Can be recursive | ⚠️ | ✅ |
Function Components
// Declaring type of props - see "Typing Component Props" for more examples type AppProps = { message: string; }; /* use `interface` if exporting so that consumers can extend */ // Easiest way to declare a Function Component; return type is inferred. const App = ({ message }: AppProps) => <div>{message}</div>; // you can choose to annotate the return type // so an error is raised if you accidentally return some other type const App = ({ message }: AppProps): JSX.Element => <div>{message}</div>; // you can also inline the type declaration; // eliminates naming the prop types but looks repetitive const App = ({ message }: { message: string }) => <div>{message}</div>;
Hooks
useState
const [user, setUser] = useState<User | null>(null); // later... setUser(newUser);
useCallback
const memoizedCallback = useCallback( (param1: string, param2: number) => { console.log(param1, param2) return { ok: true } }, [...], ); /** * VSCode will show the following type: * const memoizedCallback: * (param1: string, param2: number) => { ok: boolean } */
In React >= 18, the function signature of
useCallback changed to the following:function useCallback<T extends Function>(callback: T, deps: DependencyList): T; // @ts-expect-error Parameter 'e' implicitly has 'any' type. useCallback((e) => {}, []); // Explicit 'any' type. useCallback((e: any) => {}, []);
useEffect / useLayoutEffect
Both of
useEffectand useLayoutEffectare used for performing side effects and return an optional cleanup function which means if they don't deal with returning values, no types are necessary.function DelayedEffect(props: { timerMs: number }) { const { timerMs } = props; useEffect(() => { setTimeout(() => { /* do stuff */ }, timerMs); }, [timerMs]); // better; use the void keyword to make sure you return undefined return null; }
useRef
In TypeScript,
useRef returns a reference that is either read-only or mutable, depends on whether your type argument fully covers the initial value or not. Choose one that suits your use case.- immutable
function Foo() { // - If possible, prefer as specific as possible. For example, HTMLDivElement // is better than HTMLElement and way better than Element. // - Technical-wise, this returns RefObject<HTMLDivElement> // If you are sure that divRef.current will never be null, // it is also possible to use the non-null assertion operator !: const divRef = useRef<HTMLDivElement>(null!); useEffect(() => { // Note that ref.current may be null. This is expected, because you may // conditionally render the ref-ed element, or you may forgot to assign it if (!divRef.current) throw Error("divRef is not assigned"); // Now divRef.current is sure to be HTMLDivElement doSomethingWith(divRef.current); },[]); // Give the ref to an element so React can manage it for you return <div ref={divRef}>etc</div>; }
- mutable value ref
function Foo() { // Technically, this returns MutableRefObject<number | null> const intervalRef = useRef<number | null>(null); // You manage the ref yourself (that's why it's called MutableRefObject!) useEffect(() => { intervalRef.current = setInterval(...); return () => clearInterval(intervalRef.current); }, []); // The ref is not passed to any element's "ref" prop return <button onClick={/* clearInterval the ref */}>Cancel timer</button>; }
![[pt.1] TypeScript: References for when you forget](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fi.pinimg.com%252F564x%252Fbf%252F25%252F56%252Fbf25568477cebb29197fcfb31a76880b.jpg%3Ftable%3Dblock%26id%3D763da763-7b68-4d5e-ae3b-8973657ff08f%26cache%3Dv2&w=1200&q=75)