TypeScript'da tip toraytirish (narrowing) va type guards nima?
Savol
Tip toraytirish (type narrowing) qanday ishlaydi va type guards, jumladan o'ziga xoslari (value is Type), nima?
Qisqa javob
Narrowing — kompilyator typeof, instanceof, in yoki o'ziga xos predikat-funksiya kabi tekshiruvlar asosida keng tipni (masalan, union'ni) muayyan kod tarmog'i ichida torroq tipgacha toraytiradigan jarayon. Type guard — shunday har qanday tekshiruv, value is Type esa tip chiqarishga ta'sir qiladigan o'z predikat-funksiyangizni yozish uchun sintaksis.
Batafsil javob
TypeScript boshqaruv oqimini tahlil qiladi (control flow analysis) va tekshiruvlardan keyin o'zgaruvchining tipini avtomatik toraytiradi:
typeof — primitivlar uchun:
function format(value: string | number) {
if (typeof value === 'string') {
return value.toUpperCase() // value: string
}
return value.toFixed(2) // value: number
}
instanceof — klassga tegishlilikni tekshirish uchun:
class ApiError extends Error { constructor(public code: number) { super() } }
function handle(err: Error | ApiError) {
if (err instanceof ApiError) {
console.log(err.code) // err: ApiError
}
}
in — xususiyat mavjudligini tekshirish uchun, umumiy diskriminatorga ega bo'lmagan obyekt tiplarining birlashmasi uchun foydali:
interface Cat { meow(): void }
interface Dog { bark(): void }
function makeSound(animal: Cat | Dog) {
if ('meow' in animal) {
animal.meow() // animal: Cat
} else {
animal.bark() // animal: Dog
}
}
Discriminated union — union'ning barcha variantlarida umumiy literal maydon ("diskriminator") bo'lgan pattern, u TypeScript'ga shu maydon bo'yicha switch/if orqali tipni toraytirish imkonini beradi:
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2 // radius mavjud
case 'square': return shape.side ** 2 // side mavjud
}
}
O'ziga xos type guard'lar o'rnatilgan tekshiruvlar yetarli bo'lmaganda kerak bo'ladi — masalan, tashqi manbadan (API javobi, unknown) kelgan ma'lumotlarni murakkab validatsiya qilish uchun. Funksiya qaytariladigan tipni value is Type sifatida e'lon qiladi: agar funksiya true qaytarsa, TypeScript chaqiruv qilingan barcha joylarda argument tipini Typegacha toraytiradi:
interface User { id: number; email: string }
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'email' in value &&
typeof (value as User).id === 'number'
)
}
function processInput(data: unknown) {
if (isUser(data)) {
console.log(data.email) // data: User — o'ziga xos tekshiruv bilan toraytirildi
}
}
value is Type bo'lmasa (qaytariladigan tip sifatida oddiy boolean), TypeScript funksiya chaqirilgandan keyin argument tipini toraytirmaydi — xavfsizlikni yo'qotib, as ishlatishga to'g'ri keladi.
Misol
type ApiResponse =
| { status: 'success'; data: string[] }
| { status: 'error'; error: { code: number; message: string } }
function isSuccess(res: ApiResponse): res is Extract<ApiResponse, { status: 'success' }> {
return res.status === 'success'
}
function handleResponse(res: ApiResponse) {
// typeof / discriminated union
if (res.status === 'success') {
console.log(res.data.join(', '))
return
}
// domen xatolari uchun instanceof
const err = new Error(res.error.message)
if (err instanceof Error) {
console.error(`[${res.error.code}] ${err.message}`)
}
}
// massivni filtrlash uchun ishlatiladigan o'ziga xos type guard
const responses: ApiResponse[] = []
const successOnly = responses.filter(isSuccess) // tip: { status: 'success'; data: string[] }[]
Qo'shimcha savollar
- Type guard'dagi
isassertsdan (function assert(v): asserts v is T) nimasi bilan farq qiladi? - Narrowing yopilishlar (closures) bilan qanday ishlaydi — nega TypeScript ichki funksiya ichida toraytirishni "unutadi"?
switchda union'ning barcha variantlari qayta ishlanishini kafolatlash uchunneverorqali exhaustiveness checking ishlatish mumkinmi?