TypeScript ile React, props, state, ref'ler ve olay işleyicilerinin tiplere sahip olduğu .tsx dosyalarında bileşen yazmak demektir; böylece derleyici eksik bir prop'u ya da yanlış bir olay işleyicisini kod çalışmadan önce yakalar. Tipler derleme zamanında kaybolur: tarayıcıda çalışan şey onlar olmadan yazacağın JavaScript'in aynısıdır. Bu sayfadaki canlı editörler o JavaScript'i çalıştırır ve tipli sürüm her birinin yanında statik bir blokta durur.
İşte tiplerle aynı Badge. count isteğe bağlıdır (?), bu yüzden ikinci rozet varsayılan 0'a geri döner ve children hem bir öğeyi hem de düz bir metni kabul eder.
type BadgeProps = {
label: string;
count?: number;
children: React.ReactNode;
};
function Badge({ label, count = 0, children }: BadgeProps) {
return (
<div>
<strong>{label}</strong> ({count})
<div>{children}</div>
</div>
);
}
<Badge count={3}>Hi</Badge>;
// Error: Property 'label' is missing in type '{ children: string; count: number; }'
Bir TypeScript projesi oluşturmak
Vite'ın bir React artı TypeScript şablonu var:
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
.tsx dosyaları, strict açık bir tsconfig.json ve zaten kurulu React tip tanımlarını (@types/react, @types/react-dom) alırsın. Vite sunarken ve derlerken tipleri kaldırır ama kontrol etmez; şablonun build script'i önce tsc çalıştırır ve editörün sen yazarken kontrol eder. Next.js gibi framework'ler, CLI'larıyla bir proje oluşturduğunda TypeScript'i aynı şekilde kurar.
Props'u tiplemek
Props'u bir nesne tipi olarak tarif et ve destructure edilmiş parametreyi tiple. Birkaç kalıp çoğu bileşeni kapsar:
interface UserCardProps {
name: string; // required
age?: number; // optional, may be undefined
role: 'admin' | 'member'; // a union of allowed strings
tags: string[];
onSelect: (id: string) => void; // a callback prop
children?: React.ReactNode; // anything React can render
}
function UserCard({ name, age, role, tags, onSelect, children }: UserCardProps) {
// ...
}
type ya da interface. İkisi de props için çalışır. interface, extends ile genişletilebilir ve iki kez tanımlanarak birleştirilebilir; type union'ları, intersection'ları ve mapped type'ları da ifade edebilir. Çoğu ekip birini seçer ve her yerde onu kullanır.
children. Etiketlerin arasına giren her şey için React.ReactNode kullan: öğeler, metinler, sayılar, diziler, null. Props sayfası children'ın en başta nasıl geldiğini anlatıyor.
React.FC neden isteğe bağlı. const Badge: React.FC<BadgeProps> = (...) => ..., parametre yerine tüm fonksiyonu tipler. React 18 tiplerinden beri children'ı senin için artık eklemez, generic bir bileşen için bir tip parametresi alamaz ve tipli bir parametrenin eklemediği hiçbir şey eklemez. Eski kod onu yaygın olarak kullanır; yeni kod için tipli props'lu düz fonksiyonlar yaygın seçimdir.
useState'i tiplemek
Basit bir başlangıç değeri için çıkarım yeterlidir: useState(0) bir number, useState('') bir string'dir. State başlangıç değerinin düşündürdüğünden fazlasını tutabildiğinde bir tip argümanı aktar: null olarak başlayan bir değer ya da birkaç sabit metinden biri.
"Load user"a tıkla; durum idle'dan loading'e, oradan done'a geçer. Tipli state:
type User = { id: number; name: string };
type Status = 'idle' | 'loading' | 'done';
const [user, setUser] = useState<User | null>(null);
const [status, setStatus] = useState<Status>('idle');
user.name; // Error: 'user' is possibly 'null'
user?.name; // OK
setStatus('ready'); // Error: Argument of type '"ready"' is not assignable to parameter of type 'SetStateAction<Status>'
<User | null> olmadan useState(null) null tipini çıkarır ve setUser(data) derlenmez. Union seni boş durumu ele almaya zorlar; bu da tam olarak çalışan örnekteki user ? ... : ... kontrolüdür.
DOM öğeleri için useRef
Bir DOM öğesini gösteren bir ref öğe tipini alır ve null olarak başlar, çünkü öğe React onu ilk render'dan sonra bağlayana kadar var olmaz.
Bir şey yaz, sonra "Log the value"ya tıkla: konsol doğrudan DOM öğesinden okunan, yazdığın şeyi yazdırır. Tipli sürüm:
const inputRef = useRef<HTMLInputElement>(null);
// inputRef.current is HTMLInputElement | null
inputRef.current?.focus(); // OK
inputRef.current.focus(); // Error: 'inputRef.current' is possibly 'null'
const timerId = useRef<number | null>(null); // a mutable value, not a DOM node
Etiketle eşleşen öğe arayüzünü kullan: HTMLInputElement, HTMLDivElement, HTMLCanvasElement, HTMLButtonElement. React 19 tiplerinde useRef her zaman bir argüman alır ve current'ına atama yapabileceğin bir RefObject döndürür, bu yüzden aynı biçim DOM ref'leri için ve bir zamanlayıcı id'si gibi değerler için çalışır. Çalışma zamanı tarafı için bkz. useRef.
Olay işleyicisi tipleri
Satır içi işleyicilerin tip açıklamasına ihtiyacı yoktur: onChange={(e) => setName(e.target.value)} içinde TypeScript e'yi aktarıldığı prop'tan bilir. Bir işleyiciyi kendi fonksiyonuna taşıdığında tip ekle.
Gönder; konsol adresi kaydeder; preventDefault sayfanın yeniden yüklenmesini durdurur. Tiplerle:
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setEmail(e.target.value); // e.target is HTMLInputElement, value is string
}
function handleSubmit(e: React.SubmitEvent<HTMLFormElement>) {
e.preventDefault();
}
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {}
function handleKey(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.key === 'Enter') { /* ... */ }
}
@types/react 19.2.10'dan beri onSubmit, React.SubmitEvent ile tiplenir ve eski React.FormEvent kullanımdan kalktı olarak işaretlenir; tiplerin daha eski bir sürümüyle bunun yerine React.FormEvent<HTMLFormElement> yaz. Tip parametresi işleyicinin bağlı olduğu öğedir. Tipten emin değilsen editöründe onChange prop'unun üzerine gel ya da tüm işleyiciyi tek seferde tiple: const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => { ... }.
Context'i tiplemek
createContext'e değer tipini ver. Mantıklı bir varsayılan varsa onu aktar; her tüketici null olmayan bir değer alır:
type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light');
const theme = useContext(ThemeContext); // Theme
İyi bir varsayılan olmadığında (oturum açmış bir kullanıcı, bir store) null ile başla ve useContext'i, bir bileşen provider'ın dışında kullanılırsa hata fırlatan bir hook ile sar. Her çağıran o zaman derinlerde bir çökme yerine null olmayan tipi ve açık bir hata mesajını alır.
type Auth = { user: string; logout: () => void };
const AuthContext = createContext<Auth | null>(null);
export function useAuth(): Auth {
const value = useContext(AuthContext);
if (!value) throw new Error('useAuth must be used inside <AuthProvider>');
return value;
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState('Ada');
return (
<AuthContext value={{ user, logout: () => setUser('') }}>
{children}
</AuthContext>
);
}
<AuthContext value={...}> React 19 provider sözdizimidir; <AuthContext.Provider value={...}> da aynı şekilde tiplenir.
Generic bileşenler
Herhangi bir öğe tipiyle çalışan bir liste, tablo ya da select generic bir bileşendir: bir tip parametresi items prop'unu renderItem callback'ine bağlar.
Tipli List:
type ListProps<T> = {
items: T[];
renderItem: (item: T) => React.ReactNode;
getKey: (item: T) => string | number;
};
function List<T>({ items, renderItem, getKey }: ListProps<T>) {
return <ul>{items.map((item) => <li key={getKey(item)}>{renderItem(item)}</li>)}</ul>;
}
<List items={books} getKey={(b) => b.isbn} renderItem={(b) => b.title} />;
// b is inferred as the book type, so b.titel would be an error
TypeScript T'yi items'tan çıkarır, bu yüzden çağıranlar tip parametresini hiç yazmaz. Bir .tsx dosyasında bir ok fonksiyonu <T> yerine <T,> gerektirir, çünkü tek başına <T> bir JSX etiketi olarak okunur.
Yerel öğe props'unu genişletmek
Bir button ya da input etrafındaki bir sarmalayıcı, yerel öğenin kabul ettiği her özelliği kabul etmelidir. React.ComponentProps<'button'> o tam settir; kendi props'unu üstüne ekle ve gerisini yay.
type ButtonProps = React.ComponentProps<'button'> & {
variant?: 'primary' | 'ghost';
};
function Button({ variant = 'primary', style, ...rest }: ButtonProps) {
return (
<button
{...rest}
style={{ fontWeight: variant === 'primary' ? 600 : 400, ...style }}
/>
);
}
<Button type="submit" disabled onClick={(e) => console.log(e.currentTarget)}>
Save
</Button>;
onClick, disabled, type ve aria-* onları listelemeden tiplenir. React 19'da ref normal bir prop'tur ve ComponentProps<'button'> onu içerir, bu yüzden <Button ref={buttonRef}>, forwardRef olmadan yayma aracılığıyla DOM düğümüne ulaşır. ref'i dışarıda bırakmak istediğinde ComponentPropsWithoutRef<'button'>, kendi bileşenlerinden birinin props'unu okumak için de ComponentProps<typeof UserCard> kullan.
Yaygın tip hataları
"'ref.current' is possibly 'null'". Öğe ilk render sırasında yoktur. ref.current?.focus() kullan ya da kullanmadan önce if (ref.current) ile kontrol et.
Union state'te "Type 'string' is not assignable". const [status, setStatus] = useState('idle'), istediğinden daha geniş olan string'i çıkarır ve onu Status tipli bir prop'a aktarmak başarısız olur. useState<Status>('idle') yaz.
Yanlış öğe olarak tiplenmiş olay. Bir select üzerinde React.ChangeEvent<HTMLInputElement>, e.target'ı yanlış tip yapar. Tip parametresi işleyicinin bağlı olduğu öğeyle eşleşmelidir.
Bir hatayı susturmak için any kullanmak. O değerin dokunduğu her şey için kontrolü kapatır. unknown'u tercih edip daralt ya da tipi kaynağında düzelt.
Sıkça Sorulan Sorular
TypeScript ile React uygulaması nasıl oluşturulur?
npm create vite@latest my-app -- --template react-ts, ardından npm install ve npm run dev çalıştır. Bileşenler .tsx dosyalarına girer ve Vite derlerken tipleri çıkarır; onları kontrol etmek için tsc çalıştır (şablonun build script'i bunu yapar).
React'te children'ın tipi nedir?
React.ReactNode. React'in render edebildiği her şeyi kabul eder: öğeler, metinler, sayılar, diziler, null ve undefined. React.ReactElement'i yalnızca tam olarak tek bir öğeye ihtiyacın olduğunda kullan.
useState null ile nasıl tiplenir?
Tipi açıkça bir union olarak aktar: useState<User | null>(null). Tip argümanı olmadan TypeScript izin verilen tek değer olarak null'ı çıkarır.
React'te bir onChange olayının tipi nedir?
Bir input için React.ChangeEvent<HTMLInputElement> (o öğeler için HTMLTextAreaElement ya da HTMLSelectElement). O zaman e.target.value string olarak tiplenir.
React.FC kullanmalı mıyım?
İsteğe bağlıdır. Props parametresini doğrudan tiplemek (function Card({ title }: CardProps)) aynı işi yapar, daha sade okunur ve generic'lerle çalışır. React.FC artık children'ı örtük olarak eklemez, bu yüzden sana pek bir şey katmaz.
Props için type mı, interface mi kullanmalıyım?
İkisi de çalışır. interface genişletilebilir ve birleştirilebilir; type union'ları ve mapped type'ları ifade edebilir. Kod tabanı için bir kural seç ve ona bağlı kal.