Powszechne błędy w TypeScript w React i sposoby ich rozwiązania
Praktyczny przewodnik po pięciu często występujących kategoriach błędów w TypeScript w aplikacjach React – props, wydarzenia, stan, dane asynchroniczne oraz dzieci – z jasnymi rozwiązaniami dla każdej z nich.
Jeśli pochodzisz z świata JavaScript, TypeScript może na początku wydawać się przerażający. Przez pierwsze kilka dni może się wydawać, że kompilator aktywnie działa przeciwko tobie: czerwone i żółte linie wszędzie, tajemnicze komunikaty, które zdają się nie prowadzić nigdzie... i w pewnym momencie możesz zacząć się zastanawiać, czy przechodzenie na TypeScript rzeczywiście było dobrym pomysłem, zastanawiając się, czy powrót do zwykłego JavaScript nie byłby prostszy.
Jest jednak tak: włączenie TypeScript do kodu React to jeden z najmądrzejszych kroków, jakie możesz podjąć, jeśli zależy ci na utrzymaniu wysokiej jakości kodu na dłuższą metę.
A te błędy i ostrzeżenia, na które ciągle natrafiasz? Zdecydowanie warto z nimi walczyć – musisz tylko nauczyć się rozpoznawać powtarzające się wzorce stojące za nimi.
Większość problemów z TypeScriptem, z którymi spotykasz się podczas tworzenia aplikacji React, wcale nie jest przypadkowa. Te same nieliczne błędy pojawiają się raz za razem w różnych projektach, a po kilku razy ich rozwiązaniu zaczniesz je natychmiast dostrzegać i dokładnie wiedzieć, co oznaczają. Ten tekst omawia pięć kategorii błędów, które występują praktycznie we wszystkich projektach React opartych na TypeScriptie, wyjaśniając, dlaczego każdy z nich się pojawia i jak go rozwiązać.
Jedna rada przed rozpoczęciem: zawsze czytaj całą wiadomość o błędzie. Błędy TypeScripta mogą na pierwszy rzut oka wyglądać przerażająco, ale w rzeczywistości mają dość przewidywalną strukturę. Nie daj, by ta masa tekstu cię zdezorientowała – jeśli nie wiesz, od czego zacząć, ostatni wiersz wiadomości jest zazwyczaj najbardziej przydatnym elementem, na którym należy się skupić.
Mając to na uwadze, przejdźmy do rzeczy.
1. Błędy w atrybutach komponentów
Propy stanowią istotę każdego komponentu React, więc to naturalne, że błędy typowe związane z propami są zazwyczaj pierwszymi, na które natrafiają programiści.
Błąd: Właśćność 'X' nie istnieje w typie '{}'.
Pojawia się to, gdy używasz propu w swoim komponencie, nie określając dla niego definicji typu.
// This won't give any errors in JavaScript...
function UserCard({ name, email }) {
return (
<div>
<h3>{name}</h3>
<p>{email}</p>
</div>
);
};
// But in TypeScript...
// Error: Parameter 'name' implicitly has an 'any' type
// Error: Parameter 'email' implicitly has an 'any' type
Ten błąd pojawia się po prostu dlatego, że propom nigdy nie przypisano wyraźnych typów.
Rozwiązanie: zadeklaruj interfejs opisujący twoje propy.
interface UserCardProps {
name: string;
email: string;
};
function UserCard({ name, email}: UserCardProps) {
return (
<div>
<h3>{name}</h3>
<p>{email}</p>
</div>
);
};
Błąd: Typ 'string' nie może być przypisany do typu 'number'.
To jest dosyć proste: oznacza to, że przekazałeś prop o wartości niewłaściwego typu.
Rozwiązanie: sprawdź typ wartości, którą przekazujesz do propu.
interface ItemForSaleProps {
imgUrl: string;
itemName: string;
amount: number;
currency: string;
};
function ItemForSale({
itemName,
imgUrl,
amount,
currency
}: ItemForSaleProps) {
return (
<div class="item-for-sale">
<img src={imgUrl} alt="item image" />
<h5>{itemName}</h5>
<p>{`${currency} ${amount.toFixed(2)}`}</p>
</div>
);
};
// Error happens when passing a string where a number is expected.
<ItemForSale
imgUrl="api.example.com/image"
itemName="Sample"
amount="10.99"
currency="USD"
/>
// Should be like this
<ItemForSale
imgUrl="api.example.com/image"
itemName="Sample"
amount={10.99}
currency="USD"
/>
Czy zauważyłeś różnicę między tymi dwoma wersjami? Komponent ItemForSale oczekuje, że amount będzie typu number, więc przekazanie ciągu znaków powoduje błąd, podczas gdy prawidłowe jest przekazanie rzeczywistej wartości liczbowej. To właśnie TypeScript robi to, do czego został stworzony – wykrywa potencjalne błędy jeszcze przed uruchomieniem kodu. W tym przykładzie wywołanie .toFixed(2) na ciągu znaków '10.99' faktycznie spowodowałoby awarię w czasie wykonywania, ale TypeScript sygnalizuje ten problem już podczas kompilacji.
Błąd: W typie '{}' brakuje właściwości 'X', która jest wymagana w typie 'Props'.
Dzieje się tak, gdy właściwość jest oznaczona jako wymagana w interfejsie, ale zapomina się ją przekazać podczas używania komponentu.
interface CustomButtonProps {
label: string;
onClick: () => void;
};
function CustomButton({ label, onClick }: CustomButtonProps) {
return <button onClick={onClick}>{label}</button>;
};
// Bad usage:
<CustomButton label="Submit" />
// Error: Property 'onClick' is missing in type '{ label: string; }'
// but required in type 'ButtonProps'
Rozwiązanie: sprawdź ponownie zarówno definicje właściwości w komponencie, jak i te, które faktycznie przekazujesz podczas ich renderowania.
// Correct usage:
<CustomButton label="Submit" onClick={handleSubmit} />
Czasami masz właściwości, które nie są zawsze potrzebne. W takich przypadkach możesz oznaczyć właściwość jako opcjonalną za pomocą ?. Pamiętaj jednak, że gdy uczynisz właściwość opcjonalną, powinieneś również podać wartość domyślną lub jakiś mechanizm sprawdzania na null, aby bezpiecznie radzić sobie z jej brakiem.
interface CustomButtonProps {
label: string;
onClick: () => void;
disabled?: boolean; // this prop is now optional
className?: string; // this one too
};
function CustomButton({ label, onClick, disabled = false, className }: CustomButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={className}
>
{label}
</button>
);
};
// This component now works with or without the optional props
<CustomButton label="Submit" onClick={handleSubmit} />
<CustomButton label="Submit" onClick={handleSubmit} disabled={true} />
Teraz, gdy mamy już opcjonalne właściwości, istnieje jeszcze jeden błąd, który warto wspomnieć.
Błąd: typ ‘X | undefined’ nie może zostać przypisany do typu ‘X’.
Uczynienie właściwości opcjonalną automatycznie dodaje do jej typu undefined, co powoduje problemy, jeśli spróbujesz użyć tej wartości bez uprzedniego sprawdzenia, czy w ogóle istnieje.
interface ProfileProps {
name: string;
bio?: string;
};
function Profile({ name, bio }: ProfileProps) {
return (
<p>{name}</p>
<p>{bio.toUpperCase()}</p>
// Error: Object is possibly 'undefined'
);
};
Na ogół istnieją trzy sposoby radzenia sobie z tym problemem:
Opcja 1: podanie wartości domyślnej podczas dekonstrukcji props.
// bio is always a string, will render a default text when there's
// no bio provided
function Profile({ name, bio = 'No bio available' }: ProfileProps) {
return (
<p>{name}</p>
<p>{bio.toUpperCase()}</p>
);
};
Opcja 2: korzystanie z opcjonalnego łączenia.
// returns undefined if bio is undefined
function Profile({ name, bio }: ProfileProps) {
return (
<p>{name}</p>
<p>{bio?.toUpperCase()}</p>
);
};
Opcja 3: użycie warunkowego renderowania.
// will only render if bio exists
function Profile({ name, bio }: ProfileProps) {
return (
<p>{name}</p>
<p>{bio && bio.toUpperCase()}</p>
);
};
2. Błędy w obsługiwaczach zdarzeń
Każda komponent React, która reaguje na interakcję użytkownika, potrzebuje obsługiwaczy zdarzeń, a TypeScript oferuje bardzo precyzyjne typy dla każdego rodzaju zdarzenia DOM. Błędne określenie tych typów jest jedną z najczęstszych przyczyn zamieszania u programistów pracujących z typowanym kodem React.
Problem: parametr 'e' ma domyślnie typ 'any'.
Dzieje się tak, gdy piszemy obsługiwacz zdarzeń jako osobną funkcję poza JSX i zapominamy o adnotowaniu parametru zdarzenia.
// Error: Parameter 'e' implicitly has an 'any' type
const handleClick = (e) => {
e.preventDefault();
};
Rozwiązanie: Do parametru należy dołączyć odpowiedni typ zdarzenia React.
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
};
Warto zauważyć: gdy zapisujesz obsługę zdarzenia bezpośrednio wewnątrz JSX, TypeScript może samodzielnie określić jej typ, więc w takim przypadku ten konkretny błąd się nie pojawi.
<button onClick={(e) => {
e.preventDefault() // e is automatically React.MouseEvent<HTMLButtonElement>
}}>
Click here
</button>
Problem: W typie 'EventTarget' nie istnieje właściwość 'value'.
To prawdopodobnie najczęściej wyszukiwany błąd w TypeScriptie wśród programistów React – jest też jednym z najtrudniejszych do zrozumienia po raz pierwszy, gdy się na niego natrafia. Pojawia się, gdy próbuje się odczytać e.target.value wewnątrz obsługi zmiany.
// This will give an error because TS doesn't know target is an input element.
const handleChange = (e: React.ChangeEvent) => {
console.log(e.target.value);
// Error: Property 'value' does not exist on type 'EventTarget'
};
Podstawową przyczyną jest to, że EventTarget to ogólna interfejs DOM, więc TypeScript nie ma sposobu, by wiedzieć, że w tym konkretnym obsługiwaczu celem jest właśnie element <input>, który akurat ma dostępną właściwość value.
Rozwiązanie: Podaj konkretny typ elementu jako parametr generyczny dla typu zdarzenia.
// TypeScript now knows the target is an HTMLInputElement
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
To samo podejście sprawdza się przy innych elementach — w przypadku elementu select należy na przykład zastąpić HTMLInputElement przez HTMLSelectElement. Oto krótka lista najczęściej używanych typów zdarzeń:
// Click events
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
onClick: (e: React.MouseEvent<HTMLDivElement>) => void
// Input change events
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
// Form submission
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void
// Keyboard events
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
onKeyUp: (e: React.KeyboardEvent<HTMLInputElement>) => void
// Focus events
onFocus: (e: React.FocusEvent<HTMLInputElement>) => void
onBlur: (e: React.FocusEvent<HTMLInputElement>) => void
Ponieważ mowa o obsługiwanie zdarzeń, możesz się zastanawiać, jak poprawnie je zadeklarować podczas przekazywania ich jako właściwości do komponentów potomnych. W takim przypadku powinny być definiowane jako funkcje przyjmujące odpowiedni obiekt zdarzenia.
interface SearchInputProps {
onSearch: (e: React.ChangeEvent<HTMLInputElement>) => void;
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
};
function SearchInput({ onSearch, onSubmit }: SearchInputProps) {
return (
<form onSubmit={onSubmit}>
<input type="text" onChange={onSearch} />
<button type="submit">Search</button>
</form>
);
};
A jeśli dany obsługa zdarzeń w ogóle nie musi otrzymywać informacji o zdarzeniu, można odpowiednio uproszczyć jego typ.
interface SearchInputProps {
onClick: () => void;
onHover: () => void;
};
3. useState i useRef
Hooki stanowią istotę nowoczesnego rozwoju w React, więc nie dziwi fakt, że useState i useRef mają swoje własne specyficzności w TypeScript.
Problem: Argument typu ‘X’ nie może zostać przypisany do parametru typu ‘never’.
To jeden z najbardziej mylących błędów, na które można natrafić. Zdarza się to wtedy, gdy inicjalizujesz useState pustym tablicą, co powoduje, że TypeScript wnioskuje typ stanu jako never[]. Oto jak to wygląda:
// TypeScript infers state type as never[]
const [items, setItems] = useState([]);
// So later when we try to set a value...
setItems([{ id: 1, name: 'Item 1' }]);
// Error: Argument of type '{ id: number; name: string; }[]' is not assignable
// to parameter of type 'never[]'
Uzasadnienie: gdy początkowa wartość stanu to pusta tablica, TypeScript nie ma sposobu, aby zgadnąć, jakie elementy w końcu się w niej znajdą, więc używa domyślnego typu never[].
Rozwiązanie: Zawsze podawaj wyraźny argument typu, gdy twój początkowy stan to pusta tablica lub null.
interface Item {
id: number;
name: string;
}
// ✅ Explicitly typed
const [items, setItems] = useState<Item[]>([]);
const [selectedItem, setSelectedItem] = useState<Item | null>(null);
W tym fragmencie typ stanu selectedItem jest wyraźnie określony jako przyjmujący albo obiekt Item, albo null. Zawsze, gdy oczekujesz, że dany stan będzie początkowo pusty i zostanie wypełniony później, musisz to jasno zaznaczyć dla TypeScript – w przeciwnym razie napotkasz ten błąd:
Type 'null' is not assignable to type 'X'.
interface User {
id: number;
name: string;
email: string;
}
// TypeScript infers state as User, not User | null
const [user, setUser] = useState<User>({} as User); // Dangerous cast
// Later...
if (user.name) { /* ... */ }
// This might not catch the case where user is empty
Rozwiązanie: Użyj typu unii, który obejmuje null, aby TypeScript zrozumiał, że dane jeszcze nie zostały załadowane.
// Correctly typed, can be type User or null
const [user, setUser] = useState<User | null>(null);
// Now TypeScript forces you to handle the null case
if (user) {
console.log(user.name); // TypeScript knows user is User here
}
// Or with optional chaining:
console.log(user?.name); // string | undefined
Ta sama zasada obowiązuje, gdy stan przechowuje bardziej złożony obiekt. Załóżmy, że modelujesz stan formularza kontaktowego z kilkoma polami – właściwym podejściem jest najpierw zdefiniowanie interfejsu opisującego tę strukturę.
interface FormState {
name: string;
email: string;
message: string;
isSubmitting: boolean;
}
function SubmitMessageForm() {
const [form, setForm] = useState<FormState>({
name: '',
email: '',
message: '',
isSubmitting: false,
});
// Then do partial updates with spread operator
const handleChange = (field: keyof FormState, value: string) => {
setForm(prevState => ({ ...prevState, [field]: value }));
};
}
Problem: Obiekt może mieć wartość null.
To zdarzenie pojawia się ciągle, gdy referencja utworzona za pomocą useRef jest początkowo wartością null, a ma wskazywać na element DOM. TypeScript zaznaczy każdą próbę użycia tej referencji, zanim potwierdzi, że rzeczywiście zawiera jakąś wartość.
const inputRef = useRef<HTMLInputElement>(null);
// Typescript here knows that inputRef.current might be null
inputRef.current.focus();
// Error: Object is possibly 'null'
Rozwiązanie: sprawdź, czy current zostało ustawione, zanim go użyjesz. Gdy TypeScript dostrzeże tę kontrolę, przestanie zgłaszać zastrzeżenia, ponieważ nie może już udowodnić, że wartość może być null.
// Null check before use
const handleFocus = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
// Or if you're more into one-liners, you can use optional chaining
inputRef.current?.focus();
Zanim przejdziemy do następnej kategorii, warto zwrócić uwagę na ważną zmianę. React 19 zmienił zachowanie funkcji useRef w taki sposób, który sprawia problemy wielu programistom — konkretnie chodzi o różnicę pomiędzy RefObject<T|null> a MutableRefObject<T>. Teraz to nie wartość początkowa, którą podajemy, ale parametr typu generycznego przekazywany funkcji hook określa jej zachowanie.
Przed React 19 wywołanie useRef(null) zawsze zwracało obiekt typu MutableRefObject<T>, więc pole current mogło być zawsze przypisane na nowo.
// This worked fine
const ref = useRef<HTMLInputElement | null>(null);
ref.current = someElement;
Od wersji React 19 to typ generyczny, który określamy, decyduje o tym, czy pole current jest dostępne do zapisu.
const ref = useRef<HTMLInputElement>(null);
ref.current = someElement;
// The above will result in an error.
Tutaj, ponieważ parametr generyczny to typ elementu HTML, React 19 traktuje ref jako taki, który wskazuje na DOM, więc staje się jedynie do odczytu i jest faktycznie zarządzany przez sam React.
Jeśli zamiast tego potrzebujesz ref do przechowywania wartości zmiennej, a nie węzła DOM, postąp w ten sposób:
// Let's suppose we're building a timer
const ref = useRef<ReturnType<typeof setTimeout> | null>(null);
ref.current = setTimeout(() => {}, 1000);
To działa, ponieważ typ przekazany do generyka nie jest typem elementu HTML, więc React traktuje go jako zwykły ref zmienny — current pozostaje nadawalny.
Zasada dla React 19: gdy typ generyczny to typ elementu HTML, current staje się jedynie do odczytu i jest kontrolowany przez React. Gdy jest to inny typ, current pozostaje nadawalny. Krótko mówiąc:
useRef<HTMLElement>(null)dla węzłów DOM zarządzanych przez React (jedynie do odczytu)
useRef<T|null>(null) do przechowywania dowolnej innej zmiennej wartości, którą chcesz sam zapisać4. Asynchroniczne dane i błędy API
Gdy zaczynasz pobierać dane z API, pojawia się nowy zestaw błędów TypeScript, ponieważ pobraane dane początkowo mają typ unknown i muszą przejść przez kilka transformacji, zanim staną się czymś, co można bezpiecznie używać.
Błąd: Type 'unknown' is not assignable to type 'X'.
Domyślnie wynik wywołania fetch ma typ unknown, ponieważ TypeScript nie posiada wbudowanej wiedzy o tym, jaki kształt ma zwrócić dany endpoint.
// Fetch response is unknown
const response = await fetch('/api/users');
const data = await response.json(); // data: any (in older TS) or unknown
// So when trying to use the fetched data:
const userName = data.name;
// With the code above, you might get a warning, something like
// 'data' is of type 'unknown'
Rozwiązanie: zadeklaruj wyraźny typ dla odpowiedzi API.
interface User {
id: number;
name: string;
email: string;
};
Od tego momentu masz zazwyczaj dwie opcje dotyczące zastosowania tego typu:
// Option 1: Type assertion. Only use this when you trust the API shape.
const response = await fetch('/api/users/1');
const data = await response.json() as User;
console.log(data.name); // You'll see that you have a string here
//////////////////////////////////////////////////////////////////////////
// Option 2: Create a generic fetch wrapper, this is a safer approach
// and it's reusable.
async function fetchJSON<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json() as Promise<T>;
}
const user = await fetchJSON<User>('/api/users/1');
console.log(user.name); // TypeScript now knows this is a string
5. Dzieci i błędy JSX
Komponenty w React regularnie przyjmują i renderują children, a TypeScript oferuje kilka pokrywających się typów opisujących, czym mogą być te elementy. Znajomość różnic między nimi pomaga uniknąć wielu błędów typowych.
Trzy typy są często używane tak, jakby były ze sobą zamienne, mimo że tak nie jest:
- ReactNode
- ReactElement
- JSX.Element
Z nich ReactNode i ReactElement są rzeczywiście odrębne, natomiast JSX.Element to w rzeczywistości tylko inna nazwa dla ReactElement.
import { ReactNode, ReactElement } from 'react';
// ReactNode is the most permissive one, accepts basically
// everything React can render:
// string, number, boolean, null, undefined, ReactElement, arrays...
type ReactNode =
| ReactElement
| string
| number
| boolean
| null
| undefined
| ReactPortal
| Iterable<ReactNode>;
// ReactElement is literally a React element created by
// JSX or React.createElement
// It does not include string, number, null, undefined
type ReactElement = {
type: string | ComponentType;
props: any;
key: string | null;
};
Jako zasada ogólna: używaj ReactNode dla właściwości children, chyba że masz konkretny powód, by ograniczyć wybór do innego typu.
// Most flexible: use ReactNode for children in most cases
interface CardProps {
children: ReactNode;
};
// And use ReactElement when the requirements are not very flexible
interface StrictWrapperProps {
children: ReactElement;
};
Warto dokładniej wyjaśnić różnicę pomiędzy ReactNode a ReactElement.
ReactNode może wyglądać jak cokolwiek z tego, co następuje:
const example1 = <div>Hello</div>; // ReactElement ✅
const example2 = "Hello"; // string ✅
const example3 = 42; // number ✅
const example4 = true; // boolean ✅ (renders nothing)
const example5 = null; // null ✅ (renders nothing)
const example6 = undefined; // undefined ✅ (renders nothing)
const example7 = [<div />, <span />]; // array of ReactElements ✅
// All of the above are ReactNode
Z drugiej strony ReactElement ma większe ograniczenia:
const example1 = <div>Hello</div>;
const example2 = <Button onClick={someHandlerFn}>Submit</Button>;
const example3 = React.createElement('div', null, 'Hello');
// Under the hood, every ReactElements look like this:
{
type: 'div',
props: { children: 'Hello' },
key: null,
}
Błąd: Type 'X' is not assignable to type 'ReactNode'.
Zwykle pojawia się on, gdy próbuje się wyświetlić wartość, której TypeScript nie uznaje za ważną treść do wyświetlenia.
// Objects are not a valid React children
interface User {
name: string;
email: string;
}
// Then if you try to do this
function DisplayUser({ user }: { user: User }) {
return <div>{user}</div>;
}
// It'll probably give you an error that looks something like...
// Error: Type 'User' is not assignable to type 'ReactNode'
// Objects are not valid React Children
Rozwiązanie: wyświetlaj poszczególne pola zamiast całego obiektu.
function DisplayUser({ user }: { user: User }) {
return (
<div>
<p>{user.name}</p>
<p>{user.email}</p>
</div>
);
}
Błąd: JSX element type 'X' does not have any construct or call signatures.
Początkowo może to być mylące. Pojawia się wtedy, gdy przekazujesz wartość w jakimś miejscu, oczekując, że będzie zachowywać się jak komponent, ale TypeScript nie ma sposobu na potwierdzenie, że rzeczywiście nim jest.
// TypeScript doesn't know 'icon' is a valid component
interface ButtonProps {
icon: object; // too vague
};
// So when you try this...
function Button({ icon: Icon }: ButtonProps) {
return <Icon />;
}
// The above code will give you the error
// Error: JSX element type 'Icon' does not have any construct
// or call signatures
Rozwiązanie: zadeklaruj dynamiczne komponenty za pomocą React.ComponentType:
import { ComponentType } from 'react';
interface ButtonProps {
icon: ComponentType;
};
function Button({ icon: Icon }: ButtonProps) {
return (
<button>
<Icon />
</button>
);
}
// TypeScript now knows Icon is a valid component
Jeśli komponent ten przyjmuje również propsy, możesz je opisać wyraźnie:
interface IconProps {
size?: number;
color?: string;
};
interface ButtonProps {
icon: ComponentType<IconProps>;
};
function Button({ icon: Icon }: ButtonProps) {
return (
<button>
<Icon size={12} color="white" />
</button>
);
}
// So now the props are typed too
Praktycznym sposobem, który warto znać, jest wbudowany typ narzędziowy Reacta PropsWithChildren. Automatycznie dodaje children?: ReactNode do dowolnej interfejsu propsów, który otacza, oszczędzając ci konieczności ponownego deklarowania tego pola za każdym razem:
import { PropsWithChildren } from 'react';
// Now instead of doing this
interface CardProps {
title: string;
children?: ReactNode;
};
// You can do this
type CardProps = PropsWithChildren<{ title: string }>;
// So with this, instead of explicitly typing children manually,
// you can use the above code
function Card({ title, children }: CardProps) {
return (
<div>
<h1>{title}</h1>
<div>{children}</div>
</div>
);
}
Problem: renderowanie list elementów
TypeScript narzuca ścisłe zasady dotyczące kluczy w wyświetlanych listach, ale większość błędów typu, z którymi się spotkasz, wynika w rzeczywistości z sposobu typowania samych elementów, a nie kluczy.
// Items might be undefined, or item.id might not be a valid key type
function ItemList({ items }: { items: Item[] | undefined }) {
return (
<ul>
{
items.map(item => (
<li key={item.id}>{item.name}</li>
))
}
</ul>
);
}
// Error: Object is possibly 'undefined'
Rozwiązanie: zabezpiecz się przed wartościami undefined i upewnij się, że typowanie jest poprawne:
function ItemList({ items = [] }: { items?: Item[] }) {
return (
<ul>
{
items.map((item) => (
<li key={item.id}>{item.name}</li>
))
}
</ul>
);
}
Aby zakończyć…
Błędy TypeScript przestają być przeszkodami, gdy zaczynasz traktować je jako wskazówki. Ta zmiana następuje w momencie, gdy przestajesz reagować słowami „uff, znowu to” i zaczynasz pytać: „co tak naprawdę TypeScript próbuje mi tu powiedzieć?”
Błędy omówione w tym przewodniku to te, z którymi będziesz się stale spotykał podczas pracy z React i TypeScript. To, co odróżnia programistę, który ciągle zmaga się z systemem typów, od tego, który pracuje z nim bez problemów, zazwyczaj sprowadza się do umiejętności rozpoznawania wzorców, nic więcej. W tym momencie już znasz te wzorce.