Mundarija (27)
- Bu darsda
- 1. Nega bu kerak?
- 2. Hodisa turlari
- 2.1 Inline ishlovchi — tur kerak emas
- 2.2 Alohida funksiya — turni react dan
- 2.3 target va currentTarget
- 2.4 onSubmit: SubmitEvent va eski FormEvent
- 2.5 Hodisa prop'i — domen tilida
- 3. Native atributlarni meros qilish
- 3.1 Muammo: o'ralgan tugma atributlarni yo'qotadi
- 3.2 ComponentProps<"button"> va ...rest
- 3.3 WithRef, WithoutRef va React 19
- 3.4 HTMLAttributes — juda keng to'r
- 3.5 {...rest} qayerda: tartib muhim
- 4. Omit: native prop'ni almashtirish
- 4.1 Muammo: bir xil nom, boshqa tur
- 4.2 Bir nechta kalit va Pick
- 5. Vazifalar qadami: Tugma va useMenyu ichidagi turlar
- 6. Ko'p uchraydigan xatolar
- 7. Mashqlar
- 1-mashq (oson): qaysi tur?
- 2-mashq (o'rta): Badge — native span
- 3-mashq (qiyin): NumberField — Omit bilan
- 4-mashq: Vazifalar qadami
- 8. Real ishda
- Xulosa
- Manbalar
React + TS III: hodisalar va HTML atributlari — ChangeEvent, ComponentProps va Omit
Qisqacha: Ishlovchi JSX ichida yozilsa (
onChange={(e) => …}), TypeScript hodisa turini o'zi biladi. Alohida funksiyada esa turnireactdan import qilasiz:ChangeEvent<HTMLInputElement>,MouseEvent<HTMLButtonElement>,KeyboardEvent<…>. O'z tugmangiz yoki maydoningiz oddiy<button>ning hamma atributlarini (disabled,aria-label,type) qabul qilsin desangiz — props turiniComponentProps<"button">dan oling va...restbilan elementga yoying. Biror atributni o'zgacha qilmoqchi bo'lsangiz — avvalOmitbilan olib tashlab, keyin o'zingiznikini qo'shing.
Bu darsda
- Hodisa ishlovchilariga tur yozasiz va qachon tur kerak emasligini (kontekstli tur) ajratasiz.
targetvacurrentTargetfarqini tur darajasida tushuntira olasiz.ComponentProps<"button">,ComponentPropsWithoutRefvaHTMLAttributesfarqini bilasiz.Omitbilan native prop'ni o'z turingizga almashtirasiz:onChange(value: string).vazifalardagiTugmaqanday qilib<button>ning hamma atributini olishini o'qiysiz.
Oldin bilishingiz kerak: React + TS II, Hodisalarni boshqarish, Utility types, Komponent API'sini loyihalash.
1. Nega bu kerak?
Oldingi darsda komponent ichidagi xotiraga tur berdik. Endi komponent tashqi dunyo bilan uchrashadigan ikki joyga qaraymiz.
Birinchisi — hodisa. Sardor forma ishlovchisini alohida funksiyaga chiqardi:
function handleChange(event) {
// ❌ TS7006: Parameter 'event' implicitly has an 'any' type.
setDish(event.target.value);
}Ikkinchisi — o'ralgan element. Jasur aka «Bahor» uchun SaveButton yasadi, faqat label va onClick bilan. Bir hafta o'tib kerak bo'ldi: tugmani o'chirish (disabled), ekran o'quvchi uchun nom (aria-label), formada type="submit". Har biri uchun yangi prop qo'shish kerakmi? <button> da 200 dan ortiq atribut bor.
Hayotiy o'xshatish: elektr vilkasi. Uyingizdagi rozetka standart — har qanday vilka tushadi. Siz o'z uzaytirgichingizni yasasangiz, unga ham har qanday vilka tushishi kerak, faqat sizning chiroqingiz emas. ComponentProps<"button"> — o'z komponentingizni "standart rozetka" qilish.
2. Hodisa turlari
2.1 Inline ishlovchi — tur kerak emas
Ishlovchi JSX ichida yozilsa, TypeScript turni kutilgan joydan oladi. <input onChange> qanday funksiya kutishini u biladi, demak e ning turini ham:
import { useState } from "react";
export default function App() {
const [dish, setDish] = useState("");
return (
<div>
<input
aria-label="Taom"
onChange={(e) => setDish(e.target.value)}
/>
<p>Tanlandi: {dish}</p>
</div>
);
}Ekranda:
Tanlandi: Mantie ustiga sichqonchani olib borsangiz — ChangeEvent<HTMLInputElement, Element>. Bu — kontekstli tur (contextual typing): turni o'zgaruvchining o'zi emas, u qo'yilgan joy belgilaydi. Shuning uchun qisqa ishlovchilarni JSX ichida yozish TypeScript'da ham qulay.
2.2 Alohida funksiya — turni react dan
Ishlovchi uzun bo'lsa yoki bir necha joyda ishlatilsa, u alohida funksiyaga chiqadi. Endi kontekst yo'q — tur qo'lda yoziladi:
import { useState } from "react";
import type { ChangeEvent, MouseEvent } from "react";
export default function App() {
const [dish, setDish] = useState("");
function handleChange(event: ChangeEvent<HTMLInputElement>) {
setDish(event.target.value);
}
function handleOrder(event: MouseEvent<HTMLButtonElement>) {
const name = event.currentTarget.name;
console.log("buyurtma:", dish, "| tugma:", name);
}
return (
<div>
<input aria-label="Taom" onChange={handleChange} />
<button name="buyurtma" onClick={handleOrder}>
Buyurtma
</button>
</div>
);
}Konsolda:
buyurtma: Lag'mon | tugma: buyurtmaEkranda:
BuyurtmaHar hodisa turi — generic: <HTMLInputElement> ichidagi tur "hodisa qaysi elementda". Shuning uchun event.currentTarget.name — <button> ning name xususiyati, xatosiz. 17-qismda ko'rgan tuzoqni eslang: tur import qilinmasa, brauzerning global MouseEvent i olinadi va tsc xato beradi. import type { … } from "react" — majburiy.
Eng ko'p uchraydigan turlar:
| Hodisa prop'i | Tur | Odatiy element |
|---|---|---|
onChange |
ChangeEvent<HTMLInputElement> |
input, select, textarea |
onClick |
MouseEvent<HTMLButtonElement> |
button, a |
onKeyDown |
KeyboardEvent<HTMLInputElement> |
har qanday fokuslanadigan |
onSubmit |
SubmitEvent<HTMLFormElement> |
form |
onFocus, onBlur |
FocusEvent<HTMLInputElement> |
fokuslanadigan |
onPointerDown |
PointerEvent<HTMLDivElement> |
har qanday |
onDrop |
DragEvent<HTMLElement> |
tashlash zonasi |
Turni yod olish shart emas. Eng tez yo'l: ishlovchini avval inline yozing, e ustiga sichqonchani olib boring, ko'rgan turni nusxalang.
2.3 target va currentTarget
Hodisa tarqalishi darsidan farqni bilasiz: currentTarget — ishlovchi ulangan element, target — haqiqatan bosilgan element (ichidagi <span> ham bo'lishi mumkin). TypeScript bu farqni turda ham saqlaydi:
import type { MouseEvent } from "react";
function handleClick(event: MouseEvent<HTMLButtonElement>) {
console.log(event.target.textContent);
}
export default function App() {
return (
<button onClick={handleClick}>
<b>Osh</b> — 35000
</button>
);
}Xato:
TS2339: Property 'textContent' does not exist on type 'EventTarget'."textContent xususiyati EventTarget turida yo'q." target turi — umumiy EventTarget: u tugma ham, <b> ham, hatto matn tuguni ham bo'lishi mumkin. TypeScript buni bilmaydi va taxmin qilmaydi. currentTarget esa doim HTMLButtonElement — ishlovchini aynan unga uladingiz. Qoida: elementning xususiyati kerak bo'lsa — currentTarget. target kerak bo'lsa (delegatsiya) — avval tekshirish: if (event.target instanceof HTMLElement).
Bitta istisno — ChangeEvent. Unda target ham element turida (event.target.value xatosiz). Formadagi o'zgarish hodisasi ichma-ich bo'lmaydi, shuning uchun @types/react bu yerda target ni toraytirib bergan.
2.4 onSubmit: SubmitEvent va eski FormEvent
17-qismda forma ishlovchisini FormEvent<HTMLFormElement> deb yozgan edik. @types/react 19.3 da onSubmit ning aniqroq turi bor — SubmitEvent. Unda submitter — formani yuborgan tugma:
import type { SubmitEvent } from "react";
export default function App() {
function handleSubmit(event: SubmitEvent<HTMLFormElement>) {
event.preventDefault();
const button = event.submitter;
console.log("yuborgan tugma:", button?.textContent);
}
return (
<form onSubmit={handleSubmit}>
<button name="saqla">Saqlash</button>{" "}
<button name="chiq">Saqlab chiqish</button>
</form>
);
}Konsolda:
yuborgan tugma: Saqlab chiqishEkranda:
Saqlash Saqlab chiqishFormEvent<HTMLFormElement> bilan yozilgan eski kod ham ishlaydi — u kengroq tur, submitter siz. FormEventHandler turi esa 19.3 da eskirgan (@deprecated): @types/react izohida "bunday hodisa aslida yo'q" deb yozilgan. Yangi kodda — SubmitEvent, ChangeEvent, InputEvent.
2.5 Hodisa prop'i — domen tilida
O'z komponentingizga hodisa prop'i qo'shganda, React hodisasini tashqariga uzatish shart emas. Ota komponentga odatda ma'no kerak, DOM hodisasi emas:
// ❌ ota DOM'ni bilishga majbur
type DishPickerProps = {
onChange: (event: ChangeEvent<HTMLSelectElement>) => void;
};
// ✅ ota faqat natijani oladi
type DishPickerProps = {
onPick: (dish: string) => void;
};Ikkinchisi soddaroq va sinash oson: testda soxta hodisa yasash shart emas, onPick("Osh") yetadi. vazifalar dagi onAlmashtir(id), onOchir(id) — aynan shunday. Native element o'rami (keyingi bo'lim) — istisno: u <button> ning o'rnini bosadi, shuning uchun hodisalari ham aynan <button> nikidek.
Tekshirib ko'ring:
onClick={handleOrder}ishlaydi, lekinconst items = ["a"]; items.forEach(handleOrder)tsc xatosi beradi. Nega bir funksiya bir joyda to'g'ri, boshqasida xato?
Javob
handleOrder ning parametri MouseEvent<HTMLButtonElement>. onClick aynan shunday hodisa beradi — tur mos. forEach esa har elementni (string) uzatadi — string hodisa emas, mos kelmaydi. Turi yozilgan funksiya o'z "vilkasini" o'zi bilan olib yuradi va faqat mos "rozetkaga" tushadi.
3. Native atributlarni meros qilish
3.1 Muammo: o'ralgan tugma atributlarni yo'qotadi
Jasur akaning SaveButton iga qaytamiz:
type SaveButtonProps = { label: string; onClick: () => void };
function SaveButton({ label, onClick }: SaveButtonProps) {
return <button onClick={onClick}>{label}</button>;
}
export default function App() {
return (
<SaveButton
label="Saqlash"
onClick={() => {}}
disabled
aria-label="Buyurtmani saqlash"
/>
);
}Xato:
TS2322: Type '{ label: string; onClick: () => void; disabled: true; "aria-label": string; }' is not assignable to type 'IntrinsicAttributes & SaveButtonProps'.
Property 'disabled' does not exist on type 'IntrinsicAttributes & SaveButtonProps'."disabled SaveButtonProps da yo'q." Diqqat qiling: xato faqat disabled haqida, aria-label haqida emas. JSX'da chiziqcha bor atribut nomlari (aria-*, data-*) props turida topilmasa ham xato bermaydi — TypeScript'ning maxsus qoidasi. Bu qulay, lekin xavfli: aria-label o'tib ketdi, komponent esa uni <button> ga uzatmaydi. Malika baribir nomsiz tugmani eshitadi.
3.2 ComponentProps<"button"> va ...rest
Yechim — props turini <button> ning o'z turidan olish va qolgan hamma narsani elementga yoyish. Bu naqshni Komponent API'sini loyihalash darsida Tugma da ko'rgan edik — "o'rgatmang, uzating". U yerda maqsad API edi, bu yerda esa turlar tomoni: qaysi tur nimani beradi va qaysi biri tor.
import type { ComponentProps } from "react";
type SaveButtonProps = ComponentProps<"button"> & {
tone?: "asosiy" | "xavfli";
};
function SaveButton({ tone = "asosiy", ...rest }: SaveButtonProps) {
return <button data-tone={tone} {...rest} />;
}
export default function App() {
return (
<div>
<SaveButton onClick={() => console.log("saqlandi")}>
Saqlash
</SaveButton>{" "}
<SaveButton tone="xavfli" disabled aria-label="O'chirish">
✕
</SaveButton>
</div>
);
}Konsolda:
saqlandiEkranda:
Saqlash ✕Nima bo'ldi:
ComponentProps<"button">—<button>qabul qiladigan hamma narsa:onClick,disabled,type,aria-*,children,classNameva yana yuzlab.& { tone?: … }— o'zimizning bitta qo'shimchamiz (kesishma).{ tone, ...rest }—toneni olamiz, qolganirestga tushadi (rest).{...rest}— hammasi<button>ga.
children ham rest ichida — Saqlash matni <button> ga o'zi tushdi. Endi Jasur akaga yangi atribut kerak bo'lsa, komponentni o'zgartirish shart emas.
Tuzilmani diagrammada ko'ring — tur qanday yig'iladi:
flowchart LR
A["ComponentProps<'button'>: 200+ atribut"] --> O["Omit: o'zgartiriladiganlar olib tashlanadi"]
O --> K["& o'z props'imiz: variant, tone"]
K --> P["Props turi"]
P --> R["...rest → <button {...rest}>"]Ikkinchi tugun (Omit) — ixtiyoriy qadam, «Omit: native prop'ni almashtirish» bo'limida.
3.3 WithRef, WithoutRef va React 19
ComponentProps ning ikki ukasi bor:
| Tur | ref bormi |
Qachon |
|---|---|---|
ComponentProps<"button"> |
bor | React 19 da odatiy tanlov |
ComponentPropsWithRef<"button"> |
bor | eski kodda, forwardRef bilan |
ComponentPropsWithoutRef<"button"> |
yo'q | ref ni o'zingiz alohida yozsangiz |
React 19 gacha funksiya komponent ref ni prop sifatida ololmasdi — forwardRef kerak edi. Shuning uchun eski kodda ComponentPropsWithoutRef juda ko'p. React 19 da ref — oddiy prop (useRef II), demak ComponentProps<"button"> dagi ref ham ...rest bilan <button> ga yetib boradi.
3.4 HTMLAttributes — juda keng to'r
Internetda boshqa yozuvni ham uchratasiz: HTMLAttributes<HTMLButtonElement>. Nomiga qaraganda o'xshaydi, lekin u faqat umumiy atributlar (id, className, onClick, role). Elementga xos atributlar unda yo'q:
import type { HTMLAttributes } from "react";
function SaveButton(props: HTMLAttributes<HTMLButtonElement>) {
return <button {...props} />;
}
export default function App() {
return <SaveButton disabled>Saqlash</SaveButton>;
}Xato:
TS2322: Type '{ disabled: true; children: string; }' is not assignable to type 'IntrinsicAttributes & HTMLAttributes<HTMLButtonElement>'.
Property 'disabled' does not exist on type 'IntrinsicAttributes & HTMLAttributes<HTMLButtonElement>'.disabled, type, form — ButtonHTMLAttributes da. value, placeholder — InputHTMLAttributes da. Qaysi biri qaysi interfeysda ekanini eslab qolish shart emas: ComponentProps<"button"> to'g'ri interfeysni o'zi tanlaydi. Qoida: o'ram komponent uchun — doim ComponentProps<"teg">. HTMLAttributes<HTMLDivElement> faqat div, section kabi o'ziga xos atributi yo'q teglar uchun mos.
3.5 {...rest} qayerda: tartib muhim
JSX'da keyingi atribut oldingisini bosadi — xuddi obyekt yoyishdagidek:
import type { ComponentProps, SubmitEvent } from "react";
function SafeButton(props: ComponentProps<"button">) {
return <button type="button" {...props} />;
}
function LockedButton(props: ComponentProps<"button">) {
return <button {...props} type="button" />;
}
export default function App() {
function handleSubmit(event: SubmitEvent<HTMLFormElement>) {
event.preventDefault();
console.log("forma yuborildi:", event.submitter?.textContent);
}
return (
<form onSubmit={handleSubmit}>
<SafeButton type="submit">A</SafeButton>{" "}
<LockedButton type="submit">B</LockedButton>
</form>
);
}Konsolda:
forma yuborildi: AEkranda:
A BIkkala tugma bosildi, forma esa bir marta yuborildi. A — submit: tashqi qiymat {...props} orqali standartni bosdi. B — button: ichki qiymat oxirida turib, tashqisini bosdi. Ikkala tartib ham foydali:
- Standart qiymat — atribut
{...rest}dan oldin: chaqiruvchi xohlasa o'zgartiradi. - Majburiy qiymat — keyin: chaqiruvchi o'zgartira olmaydi.
className alohida holat: uni bosish emas, qo'shish kerak ("tugma " + className). Aks holda chaqiruvchi o'z klassini qo'shganda komponentning asosiy klassi yo'qoladi.
4. Omit: native prop'ni almashtirish
4.1 Muammo: bir xil nom, boshqa tur
Taom nomi maydoni uchun TextField yasaymiz. Ota komponentga hodisa emas, matn kerak: onChange(value: string). Lekin ComponentProps<"input"> da onChange allaqachon bor va u hodisa kutadi. Kesishma bilan ikkalasini birlashtirsak — tur ichida ziddiyat paydo bo'ladi: onChange bir vaqtda ikki xil funksiya bo'lishi kerak. Natijada bu prop'ni to'g'ri ishlatib bo'lmaydi.
Yechim — avval Omit bilan native onChange ni olib tashlash, keyin o'zimiznikini qo'shish:
import { useState } from "react";
import type { ComponentProps } from "react";
type TextFieldProps = Omit<ComponentProps<"input">, "onChange"> & {
label: string;
onChange: (value: string) => void;
};
function TextField({ label, onChange, id, ...rest }: TextFieldProps) {
const inputId = id ?? "maydon";
return (
<p>
<label htmlFor={inputId}>{label}</label>{" "}
<input
id={inputId}
{...rest}
onChange={(e) => onChange(e.target.value)}
/>
</p>
);
}
export default function App() {
const [dish, setDish] = useState("");
return (
<div>
<TextField
label="Taom nomi"
maxLength={40}
onChange={setDish}
/>
<p>Yangi taom: {dish}</p>
</div>
);
}Ekranda:
Taom nomi
Yangi taom: ChuchvaraUch narsaga qarang:
Omit<…, "onChange">— native turdan bitta kalit olib tashlandi, qolgan 200+ atribut (maxLength,placeholder,required) joyida.onChange={setDish}—setDishto'g'ridan-to'g'ri uzatildi. U satr qabul qiladi, bizningonChangeham satr beradi. Ota komponente.target.valueni bilmaydi.onChange{...rest}dan keyin yozildi.restda endionChangeyo'q (destrukturizatsiya qildik), lekin tartib baribir "bu qiymat bosilmaydi" degan niyatni ko'rsatadi.
label — majburiy prop. Har maydonda yorliq bo'lishini tur kafolatlaydi: yorliqsiz <TextField onChange={…} /> — tsc xatosi. Bu — a11y'ni tur bilan himoyalash. Keyingi darsda bu g'oyani Tugma ga qo'llaymiz.
Tekshirib ko'ring:
TextFieldPropsdaOmitni olib tashlasak (ComponentProps<"input"> & { onChange: (value: string) => void }),onChange={setDish}qanday xato beradi?
Javob
Kesishmada onChange ning turi ikkalasining kesishmasi bo'ladi: funksiya ham hodisani, ham satrni qabul qila olishi kerak. setDish faqat satr (yoki updater funksiya) qabul qiladi. Hodisani qabul qilmaydi — tsc TS2322 beradi. Ichkarida esa onChange(e.target.value) chaqiruvi ham shubhali bo'ladi. Omit ziddiyatni ildizidan olib tashlaydi.
4.2 Bir nechta kalit va Pick
Omit ga bir necha kalitni union bilan berish mumkin: Omit<ComponentProps<"input">, "onChange" | "value" | "type">. Teskarisi — Pick: faqat kerakli kalitlarni olish. Pick<ComponentProps<"input">, "placeholder" | "maxLength"> — komponent faqat shu ikkitasini oladi. Pick — "kam va aniq" API (Komponent API'sini loyihalash), Omit — "deyarli hammasi, bittasi o'zgacha".
5. Vazifalar qadami: Tugma va useMenyu ichidagi turlar
refactor/turlar da bugun ham kod o'zgarmaydi. Bugungi darsning ikki g'oyasi vazifalar da 18/#30–#31 dan beri bor. Avval ui/Tugma.tsx ni qadam boshidagi holatida o'qiymiz (holat 33, kanondan aynan):
import type { ComponentPropsWithoutRef, Ref } from "react";
type TugmaVarianti = "oddiy" | "asosiy" | "xavfli";
type Props = ComponentPropsWithoutRef<"button"> & {
variant?: TugmaVarianti;
ref?: Ref<HTMLButtonElement> | undefined;
};
export function Tugma({
variant = "oddiy",
type = "button",
className,
ref,
...qolgan
}: Props) {
return (
<button
ref={ref}
type={type}
className={
className === undefined ? "tugma" : `tugma ${className}`
}
data-variant={variant === "oddiy" ? undefined : variant}
{...qolgan}
/>
);
}Bugungi darsning hammasi shu yerda:
ComponentPropsWithoutRef<"button">+ o'zrefi. NatijaComponentProps<"button">bilan bir xil. Kanonrefni alohida yozishni tanladi — tur o'qilganda "ref qabul qiladi" ko'rinib tursin.| undefined— o'tgan darsdagiexactOptionalPropertyTypesuchun.type = "button"— standart qiymat, va u{...qolgan}dan oldin. Formadagi tugma tasodifansubmitbo'lmaydi, kerak bo'lsa chaqiruvchitype="submit"yozadi.className— bosilmaydi, qo'shiladi.data-variant— ko'rinish CSS'da (Dizayn tizimi asoslari).
Endi ui/useMenyu.ts dagi prop getter. Ishlovchilar JSX'dan tashqarida — obyekt ichida yozilgan, shuning uchun kontekst yo'q va turlar qo'lda (qisqartirilgan parcha):
import type { KeyboardEvent, MouseEvent } from "react";
tugmaXossalari: () => ({
ref: tugmaRef,
type: "button" as const,
"aria-haspopup": "menu" as const,
"aria-expanded": ochiq,
onKeyDown: (event: KeyboardEvent<HTMLButtonElement>) => {
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
event.preventDefault();
och(event.key === "ArrowDown" ? "birinchi" : "oxirgi");
}
},
}),as const nega kerak? Usiz type: "button" ning turi string bo'lib qolardi. Obyekt <button {...menyu.tugmaXossalari()}> ga yoyilganda tsc string ni "button" | "submit" | "reset" ga bera olmasdi. Xuddi shunday "aria-haspopup" ham: unga faqat "menu", "listbox" kabi aniq qiymatlar mumkin.
Tekshirish — npm run tip: 0 xato. Bugun commit yo'q. Keyingi darsda Tugma ning props turiga Omit kiradi — tugmaning nomi bo'lishi shart bo'ladi.
6. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
TS7006: Parameter 'event' implicitly has an 'any' type |
alohida ishlovchida tur yo'q | ChangeEvent<HTMLInputElement> kabi tur |
KeyboardEvent<…> is missing … isComposing |
tur react dan import qilinmagan |
import type { KeyboardEvent } from "react" |
Property 'x' does not exist on type 'EventTarget' |
event.target |
currentTarget yoki instanceof |
Property 'disabled' does not exist |
o'ramda native props yo'q | ComponentProps<"button"> |
disabled HTMLAttributes da yo'q |
juda keng tur | ComponentProps<"teg"> |
aria-label jim yo'qoldi |
chiziqchali nom tekshirilmaydi, {...rest} yo'q |
rest ni elementga yoying |
onChange bilan ziddiyat |
kesishmada bir xil kalit | Omit<…, "onChange"> |
7. Mashqlar
Mashqlar kurs/mashqlar/18/36-hodisalar-atributlar/ da.
1-mashq (oson): qaysi tur?
Ishlovchi alohida funksiyada. Turni yozing.
<select onChange={handlePick}>—handlePick(event: )<form onSubmit={handleSend}>—handleSend(event: )<textarea onKeyDown={handleKey}>—handleKey(event: )
Yechim
ChangeEvent<HTMLSelectElement>—event.target.valuetanlangan<option>qiymati.SubmitEvent<HTMLFormElement>—FormEvent<HTMLFormElement>ham qabul qilinadi (kengroq tur), lekinsubmitterfaqatSubmitEventda.KeyboardEvent<HTMLTextAreaElement>— element turitextarea,inputemas. Noto'g'ri element bersangiz, tsconKeyDown={handleKey}qatorida xato beradi.
Hammasi import type { … } from "react" bilan.
2-mashq (o'rta): Badge — native span
Badge komponenti <span> ning hamma atributlarini qabul qilsin va qo'shimcha tone?: "yashil" | "qizil" (standart "yashil"). className qo'shilsin ("badge" + tashqi klass), tone — data-tone atributiga. Ishora: ComponentProps<"span">, destrukturizatsiyada className va tone ni ajrating («{...rest} qayerda: tartib muhim» bo'limi).
export default function App() {
return (
<p>
Osh <Badge>yangi</Badge>{" "}
<Badge tone="qizil" title="Bugun tugadi" className="kichik">
tugadi
</Badge>
</p>
);
}Yechim
import type { ComponentProps } from "react";
type BadgeProps = ComponentProps<"span"> & {
tone?: "yashil" | "qizil";
};
function Badge({ tone = "yashil", className, ...rest }: BadgeProps) {
const classes =
className === undefined ? "badge" : `badge ${className}`;
return <span className={classes} data-tone={tone} {...rest} />;
}
export default function App() {
return (
<p>
Osh <Badge>yangi</Badge>{" "}
<Badge tone="qizil" title="Bugun tugadi" className="kichik">
tugadi
</Badge>
</p>
);
}Ekranda:
Osh yangi tugadititle, children — rest orqali <span> ga yetdi, Badge ularni sanab o'tirmadi. className destrukturizatsiyada olingani uchun rest da yo'q — {...rest} uni bosib ketolmaydi. Natijada ikkinchi badge klassi — "badge kichik".
3-mashq (qiyin): NumberField — Omit bilan
Mehmonlar soni maydoni. Talablar:
- Props —
<input>ning hamma atributlari, lekintype,value,onChangeo'zimizniki:value: number,onChange: (value: number) => void.typeumuman berilmaydi (doim"number"). label: string— majburiy,<label htmlFor>bilan bog'langan.App—useState(2), maydonmin={1},max={12}, ostidaMehmonlar: N.
Ishora: Omit<…, "type" | "value" | "onChange">; e.target.valueAsNumber — son (Formalar I).
Yechim
import { useState } from "react";
import type { ComponentProps } from "react";
type NumberFieldProps = Omit<
ComponentProps<"input">,
"type" | "value" | "onChange"
> & {
label: string;
value: number;
onChange: (value: number) => void;
};
function NumberField(props: NumberFieldProps) {
const { label, value, onChange, id = "son", ...rest } = props;
return (
<p>
<label htmlFor={id}>{label}</label>{" "}
<input
{...rest}
id={id}
type="number"
value={value}
onChange={(e) => onChange(e.target.valueAsNumber)}
/>
</p>
);
}
export default function App() {
const [guests, setGuests] = useState(2);
return (
<div>
<NumberField
label="Mehmonlar"
min={1}
max={12}
value={guests}
onChange={setGuests}
/>
<p>Mehmonlar: {guests}</p>
</div>
);
}Ekranda:
Mehmonlar
Mehmonlar: 5type="number" {...rest} dan keyin — lekin Omit tufayli rest da type umuman yo'q, chaqiruvchi uni bera olmaydi (<NumberField type="text" …> — tsc xatosi). min, max esa native — rest orqali o'tdi. Bo'sh maydonda valueAsNumber — NaN. Haqiqiy formada buni tekshirish kerak: Number.isNaN(n) ? … : onChange(n).
4-mashq: Vazifalar qadami
Tugma.tsx va useMenyu.ts ni o'qing. Savollarga javob bering: (a) Tugma ga type berilmasa nima bo'ladi va nega bu standart xavfsiz? (b) className="kichik" berilsa, <button> ning class atributi qanday? (c) useMenyu dagi type: "button" as const dan as const olib tashlansa, qaysi buyruq yiqiladi?
Yechim
- (a)
type="button"— formada tugma bosilganda forma yuborilmaydi. HTML'da<button>ning standartisubmit, bu ko'p xatoga sabab bo'ladi (Formalar I). - (b)
"tugma kichik"—classNameqo'shiladi, bosilmaydi. - (c)
npm run tip(tsc):{...tugmaXossalari()}yoyilgan joydastringturini"button" | "submit" | "reset" | undefinedga berib bo'lmaydi. Lint va test o'tishi mumkin, tur tekshiruvi — yo'q.
8. Real ishda
- Dizayn tizimlari (shadcn/ui, Radix, MUI) — har tugma, maydon
ComponentProps<"button">yoki shunga o'xshash tur bilan. Ularning kodini ochsangiz, bugungi...propsnaqshini ko'rasiz. - Forma kutubxonalari (React Hook Form) maydonga
{...register("ism")}yoyadi — bu ham prop getter, ichidaonChange,onBlur,ref,name(RHF). - Kod ko'rigida tez-tez uchraydigan izoh: "bu ishlovchi DOM hodisasini otaga uzatyapti —
onPick(value)qiling". - Intervyu savollari: "
targetvacurrentTargetturlari nega farq qiladi?", "ComponentPropsvaHTMLAttributesfarqi?", "native prop'ni qanday almashtirasiz?" (javob:Omit).
Xulosa
- Inline ishlovchida tur kerak emas (kontekstli tur); alohida funksiyada —
reactdanChangeEvent,MouseEvent,KeyboardEvent,SubmitEvent. currentTarget— ishlovchi ulangan element (aniq tur),target—EventTarget(tekshirish kerak).- O'ram komponent —
ComponentProps<"teg">+...rest;HTMLAttributeselementga xos atributlarni bilmaydi. {...rest}dan oldin — standart, keyin — majburiy qiymat;className— qo'shiladi.- Native prop'ni o'zgacha qilish —
Omit<…, "onChange"> & { onChange: … }. vazifalar:Tugma—ComponentPropsWithoutRef<"button">+ref,type="button"standart;useMenyu—as constva qo'lda hodisa turlari.
Keyingi dars: React + TS IV: generic, polymorphic va discriminated union props — generic komponentlar va bir-birini istisno qiladigan props.
Manbalar
- React: "Using TypeScript — Typing DOM events" — react.dev/learn/typescript (2026-10 holatiga, React 19.3)
@types/react19.3.0 —SubmitEvent,ChangeEvent,FormEventHandler(@deprecated),ComponentPropsta'riflari- TypeScript: JSX — "attribute names that are not valid identifiers" — typescriptlang.org/docs/handbook/jsx.html
- MDN:
SubmitEvent.submitter— developer.mozilla.org/docs/Web/API/SubmitEvent/submitter
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!