reactreact with typescript

TypeScript Basics

Typing Variables

Assign types with : after the variable name.

let name: string = "John Doe";

TypeScript can also infer the type if it’s just a simple assignment.

let name = "John Doe"; // TypeScript infers the type as string
name = 123; // Error: Type '123' is not assignable to type 'string'

Typing Functions

Specify the type of the function parameters inside the parentheses, and the return type after the parentheses.

function add(a: number, b: number): number {
  return a + b;
}

Optional Parameters

Use ? to make a parameter optional.

type MyComponentProps = { name?: string };

any Type

You can use the any type to opt out of type checking.

let x: any = 123;

Union Types

Use | to specify multiple types.

let x: string | number = 123;

Extracting Types

You can extract types into a separate type.

type Color = "red" | "green" | "blue";
 
type ComponentProps = {
  color: Color;
};

Typing Arrays

Use [] to specify an array of a certain type.

let names: string[] = ["John", "Doe"];

Tuple Types

Use [] with a specific type for each element. You specify the type of each element in the tuple.

let user: [string, number] = ["John Doe", 30];

Record Type

Use Record to define an object with a specific key and value type.

type MyComponentProps = {
  borderRadius: Record<string, number>;
};

Type Alias vs Interface

You don’t have to write the equals sign when using interface.

inteface MyComponentProps {
  name: string;
}

With interface, you can only define an object type. For example, the following isn’t possible with interface.

type URL = string;
 
const url: URL = "https://example.com";

You also can’t use | to define union types with interface.

type Color = "red" | "green" | "blue";

as const

Use as const to type the array as readonly and immutable.

const colors = ["red", "green", "blue"] as const;

Omit Type

Use Omit to exclude certain properties from a type.

type User = {
  name: string;
  age: number;
  email: string;
};
 
type UserWithoutEmail = Omit<User, "email">;

as Type Assertion

Use as to assert the type of a variable.

const name = "John Doe" as string;

Generics

Use generics to ensure corresponding types.

const covertToArray = <T,>(value: T): T[] => [value];

or using a traditional function declaration.

function covertToArray<T>(value: T): T[] {
  return [value];
}

Type parameters can also be used to write type aliases.

type ButtonProps<T> = {
  countValue: T;
  countHistory: T[];
};
 
export default function Button<T>({
  countValue,
  countHistory,
}: ButtonProps<T>) {
  return (
    <div>
      <div>{countValue}</div>
      <div>{countHistory.join(", ")}</div>
    </div>
  );
}

Intersection Types

Use & to combine multiple types.

type ButtonProps = {
  type: "button" | "submit" | "reset";
  color: "red" | "green" | "blue";
};
 
type SuperButtonProps = ButtonProps & {
  size: "small" | "medium" | "large";
};

Interface Extends

Use extends to extend an interface.

interface ButtonProps {
  type: "button" | "submit" | "reset";
  color: "red" | "green" | "blue";
}
 
interface SuperButtonProps extends ButtonProps {
  size: "small" | "medium" | "large";
}

Typing Props in React Components

Typically you only type the props, because TypeScript can infer the return type.

export default function MyComponent(props: { name: string }) {
  return <div>{props.name}</div>;
}

You can immediately destructure the props object.

export default function MyComponent({ name }: { name: string }) {
  return <div>{name}</div>;
}

You can also extract the props into a separate type.

type MyComponentProps = { name: string };
 
export default function MyComponent({ name }: MyComponentProps) {
  return <div>{name}</div>;
}

If you have default values, you don’t need to specify the type.

export default function MyComponent({ name = "John Doe" }) {
  return <div>{name}</div>;
}

In the past, you would use React.FC to type a functional component.

const MyComponent: React.FC<{ name: string }> = ({ name }) => {
  return <div>{name}</div>;
};

React.CSSProperties

Use React.CSSProperties to type CSS properties.

type MyComponentProps = {
  style: React.CSSProperties;
};
 
export default function MyComponent({ style }: MyComponentProps) {
  return <div style={style}>Hello</div>;
}

Typing Functions

Use => to specify the function type.

type MyComponentProps = {
  onClick: () => void;
};

If the function takes in parameters and returns a value, you can specify the types.

type MyComponentProps = {
  onClick: (test: string) => number;
};

Typing Children

Use ReactNode to type children.

type MyComponentProps = {
  children: React.ReactNode;
};
 
export default function MyComponent({ children }: MyComponentProps) {
  return <div>{children}</div>;
}

Typing JSX Elements

Use JSX.Element to type JSX elements. This is more specific than ReactNode.

type MyComponentProps = {
  children: JSX.Element;
};

Typing useState setter function

Use SetStateAction to type the setter function of useState.

import { SetStateAction } from "react";
 
type MyComponentProps = {
  setCount: React.Dispatch<SetStateAction<number>>;
};

Tip: hover over the useState function to see the type of the setter function.

ComponentProps

Use ComponentProps to define the props of a native component.

type ButtonProps = React,ComponentProps<"button">;
type LinkProps = React,ComponentProps<"a">;

Prefer ComponentPropsWithRef if you need to forward refs.

type ButtonProps = React,ComponentPropsWithRef<"button">;
type LinkProps = React,ComponentPropsWithRef<"a">;

Or use ComponentPropsWithoutRef if you don’t need to forward refs.

type ButtonProps = React,ComponentPropsWithoutRef<"button">;
type LinkProps = React,ComponentPropsWithoutRef<"a">;

Rest Parameters

Use ... to specify rest parameters.

export default function Button({ type, autoFocus, ...rest }: ButtonProps) {
  return <button type={type} autoFocus={autoFocus} {...rest} />;
}

Typing Event Handlers

Hover over the event to see the type, and copy it.

type MyComponentProps = {
  onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
};

Typing useState

Use generics to specify the type of the state.

const [count, setCount] = useState<number>(0);

But this is redundant, because TypeScript can infer the type.

However, if you want to specify the state for a complex object, you can use generics.

type User = {
  name: string;
  age: number;
};
 
const [user, setUser] = useState<User>({ name: "John Doe", age: 30 });

To take care of the initial null value, use the pipe operator.

const [user, setUser] = useState<User | null>(null);

Typing useRef

Use generics to specify the type of the ref.

const inputRef = useRef<HTMLInputElement>(null);

Using HTMLInputElement is more specific than using HTMLElement.

Import type

Do not use index.d.ts to import types, which is usually used for third party libraries. Use something like types.ts instead and put it in the lib folder.

import { type User } from "@/lib/types";

unknown Type

useEffect(() => {
  fetch("https://api.example.com")
    .then((response) => response.json())
    .then((data: unknown) => {
      // use a schema validation library like Zod to validate the shape
      // const parsedData = toDoSchema.parse(data);
    });
}, []);