TypeScript / Conditional Types

Conditional Types

TypeScript-д нөхцлөөс хамааран өөр өөр төрөл буцаах боломжтой. Энэ онцлогийг conditional type гэдэг бөгөөд үүний синтакс нь JavaScript-ийн тернар оператортай (? :) ижил харагдана.

Үндсэн синтакс

typescript
T extends U ? X : Y

"Хэрэв T нь U-г өргөтгөж байвал X төрлийг буцаа, үгүй бол Y төрлийг буцаа" гэсэн утгатай:

typescript
// Энгийн жишээ
type МөрЭсэх<T> = T extends string ? true : false;

type Тест1 = МөрЭсэх<string>; // true
type Тест2 = МөрЭсэх<number>; // false
type Тест3 = МөрЭсэх<"Монгол">; // true — string literal мөн string-г өргөтгөнө

// Тоо эсэхийг шалгах
type ТооЭсэх<T> = T extends number ? "тоо" : "тоо биш";

type Ш1 = ТооЭсэх<42>; // "тоо"
type Ш2 = ТооЭсэх<"hello">; // "тоо биш"
type Ш3 = ТооЭсэх<boolean>; // "тоо биш"

Conditional type-г функцтэй хослуулах

Conditional type нь generic-тэй хослуулах үед хамгийн хүчтэй болдог:

typescript
// Массив бол элементийн төрлийг, үгүй бол тухайн төрлийг буцаана
type МассивНь<T> = T extends (infer Item)[] ? Item : T;

type Тест1 = МассивНь<string[]>; // string
type Тест2 = МассивНь<number[]>; // number
type Тест3 = МассивНь<boolean>; // boolean — массив биш тул өөрийгөө буцаана

// Promise бол дотоод төрлийг задлах
type PromiseНь<T> = T extends Promise<infer R> ? R : T;

type Тест4 = PromiseНь<Promise<string>>; // string
type Тест5 = PromiseНь<Promise<number>>; // number
type Тест6 = PromiseНь<boolean>; // boolean

Distributive conditional types

Union төрлөөр T дамжуулахад conditional type тус тусдаа хэрэглэгддэг:

typescript
type НullГүй<T> = T extends null | undefined ? never : T;

type Тест1 = НullГүй<string | null | undefined>;
// string — null ба undefined хасагдаж string л үлдэнэ

type Тест2 = НullГүй<number | null>;
// number

// Яаж ажилладагыг задалж харвал:
// НullГүй<string | null | undefined>
// = НullГүй<string> | НullГүй<null> | НullГүй<undefined>
// = string         | never          | never
// = string

never нь union-д нэмэгдэхэд автоматаар хасагддаг — тиймээс string | never гэдэг нь зүгээр string болдог.

Бодит жишээ: функцийн буцаах төрлийг авах

typescript
// Функц бол буцаах төрлийг, үгүй бол never буцаана
type БуцаахТөрөл<T> = T extends (...args: any[]) => infer R ? R : never;

function нэмэх(а: number, б: number): number {
  return а + б;
}

function мэндчилэх(нэр: string): string {
  return `Сайн уу, ${нэр}!`;
}

type Нэмэхийн_буцаалт = БуцаахТөрөл<typeof нэмэх>; // number
type Мэндчилэхийн_буцаалт = БуцаахТөрөл<typeof мэндчилэх>; // string

// TypeScript-д суурилсан ReturnType<T> яг ингэж бичигдсэн
type МорьТестлэх = ReturnType<typeof нэмэх>; // number

Nested conditional type

Conditional type-г давхарлаж болно — JavaScript-ийн if...else if...else шиг:

typescript
type ТөрлийнНэр<T> = T extends string
  ? "string"
  : T extends number
    ? "number"
    : T extends boolean
      ? "boolean"
      : T extends null
        ? "null"
        : T extends undefined
          ? "undefined"
          : "object";

type Тест1 = ТөрлийнНэр<"Монгол">; // "string"
type Тест2 = ТөрлийнНэр<42>; // "number"
type Тест3 = ТөрлийнНэр<true>; // "boolean"
type Тест4 = ТөрлийнНэр<null>; // "null"
type Тест5 = ТөрлийнНэр<{}>; // "object"

Conditional type нь TypeScript-ийн хамгийн нарийн онцлогуудын нэг. Эхлээд ойлгоход хэцүү мэт санагдаж болох ч хэрэглэж дадсанаар кодын илэрхийлэх чадвар эрс нэмэгдэнэ.

Дараагийн хичээлд:

Template Literal Types — мөрийн загвар дээр суурилсан шинэ төрлүүдийг хэрхэн үүсгэх, property нэрийг автоматаар хувиргахыг сурна.