TypeScript'da tip toraytirish (narrowing) va type guards nima?

MiddleTypeScript #typescript #narrowing #guards

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 is assertsdan (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 uchun never orqali exhaustiveness checking ishlatish mumkinmi?