NANDHOO.

TypeScript with React: Industry Standard UI

TypeScript with React: Industry Standard UI


TypeScript and React are a match made in heaven. React handles the UI components, while TypeScript ensures the data flowing between those components is correct. This combination virtually eliminates "prop-drilling" errors and "missing property" crashes.




1. Typing Functional Components


In modern React, we use Functional Components. You can type them using React.FC (Functional Component) or by typing the props directly.


interface UserProfileProps {
    name: string;
    age: number;
    isAdmin?: boolean; // Optional
}

// Option A: Direct typing (Recommended for simplicity) export const UserProfile = ({ name, age, isAdmin }: UserProfileProps) => { return

{name} - {age}
; };


// Option B: React.FC (Includes 'children' by default in older versions) const Card: React.FC<{ title: string }> = ({ title, children }) => (

{title}

{children}
);




2. Typing Hooks


useState

TypeScript is usually good at inferring the type from the initial value. If the initial value is null or an object, you should provide a generic.


const [count, setCount] = useState(0); // Inferred as number
const [user, setUser] = useState<User | null>(null); // Generic type

useRef

Crucial for DOM elements or persisting values.

const inputRef = useRef<HTMLInputElement>(null);

const focusInput = () => { inputRef.current?.focus(); };




3. Handling Events


Typing events correctly gives you full IntelliSense for event properties (like e.target.value).


const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
};

const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); };




4. Complex State with useReducer


For complex state logic, useReducer combined with Discriminated Unions is the ultimate pattern.


type Action = 
    | { type: "increment"; amount: number }
    | { type: "decrement" };

interface State { count: number; }


function reducer(state: State, action: Action): State { switch (action.type) { case "increment": return { count: state.count + action.amount }; case "decrement": return { count: state.count - 1 }; default: return state; } }




5. Typing Children and Layouts


If you create layout components, you need to type the children prop.


  • React.ReactNode: Most flexible. Includes strings, numbers, elements, or arrays.
  • React.ReactElement: Only includes a single JSX element.

interface LayoutProps {
    children: React.ReactNode;
}

const Layout = ({ children }: LayoutProps) => (

{children}
);




6. Summary


  • Type your Props to define component interfaces.
  • Use Generics with Hooks when inference isn't enough.
  • React.ChangeEvent and other built-in types make event handling easy.
  • Discriminated Unions are perfect for reducers.