Загрузка…
Загрузка…
TypeScript · middle · сложность 5
Типизируйте объект props напрямую и остальное отдайте inference: без React.FC, useState<T> — только когда init слишком слаб для вывода, а каждому useRef и reducer дайте явный тип — именно там inference тихо врёт.
Компонент — (props: Props) => JSX. React.FC устарел (implicit children, боль с generics). События — ChangeEvent/FormEvent. children: ReactNode.
Hooks: примитивы — без дженерика; null/[] — с дженериком. useRef<HTMLInputElement>(null). useReducer — явные state/action (часто discriminated union). Context — создавайте с типом или undefined + guard.
// ❌ implicit children, awkward generics
const Avatar: React.FC<Props> = ({ src }) => <img src={src} />;
// ✅ explicit, stricter, generics-friendly
function Avatar({ src }: Props) { return <img src={src} />; }const [user, setUser] = useState<User | null>(null); // ✅ union is explicitimport { useState, useRef, useReducer } from "react";
// --- Props: type the object, derive children intent explicitly ---
type ButtonProps = {
variant: "primary" | "ghost";
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
children: React.ReactNode; // opt IN to children deliberately
};
function Button({ variant, onClick, children }: ButtonProps) {
return <button className={variant} onClick={onClick}>{children}</button>;
}
// --- Extend native element props without re-typing them all ---
type InputProps = React.ComponentPropsWithoutRef<"input"> & {
label: string; // your extras on top of every real <input> attribute
};
// --- Refs: element type so .current is typed; null init = read-only DOM ref
const inputRef = useRef<HTMLInputElement>(null);
inputRef.current?.focus(); // ✅ typed, null-guarded
// --- useReducer with a discriminated action union → exhaustive switch ---
type State = { count: number };
type Action = { type: "inc" } | { type: "add"; by: number };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "inc": return { count: state.count + 1 };
case "add": return { count: state.count + action.by }; // action.by is known
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
// --- Generic component: the pattern React.FC makes ugly ---
function List<T>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }) {
return <ul>{items.map((it, i) => <li key={i}>{render(it)}</li>)}</ul>;
}Props на параметре функции, точечные дженерики у hooks, без React.FC — современный дефолт типизации React+TS.