Generic үндэс
Кодоо бичихдээ "энэ функц зөвхөн тоонуудад биш, дурын төрлийн өгөгдөлд ажиллана" гэх мэт нөхцөл байдал байнга тохиолддог. TypeScript-ийн generic нь яг энэ асуудлыг шийддэг — нэг удаа бичиж, олон өөр төрлийн өгөгдөлд дахин дахин ашиглаж болох кодыг бичих арга юм.
Generic гэж юу вэ?
Generic-гүйгээр нэг функцийг олон төрлөөр бичвэл хэрхэн давтагдаж байдгийг үзье:
// Давтамжтай — төрөл тус бүрд тусдаа функц
function анхны_тоо(массив: number[]): number {
return массив[0];
}
function анхны_мөр(массив: string[]): string {
return массив[0];
}
function анхны_логик(массив: boolean[]): boolean {
return массив[0];
}
// Generic ашиглавал нэг функц хангалттай
function анхны<T>(массив: T[]): T {
return массив[0];
}
console.log(анхны([1, 2, 3])); // 1 — T = number
console.log(анхны(["а", "б", "в"])); // "а" — T = string
console.log(анхны([true, false, true])); // true — T = boolean
T бол төрлийн параметр (type parameter). Ямар ч үсэг хэрэглэж болох ч T (Type-ийн товчлол) заншил болсон байдаг.
T гэдэг юуг илэрхийлэх вэ?
T нь функцийг дуудах үед TypeScript автоматаар тодорхойлдог "хоосон зай" юм:
function боох<T>(утга: T): { өгөгдөл: T; огноо: string } {
return {
өгөгдөл: утга,
огноо: new Date().toISOString(),
};
}
const боогдсон_тоо = боох(42);
// { өгөгдөл: number, огноо: string }
const боогдсон_мөр = боох("Монгол");
// { өгөгдөл: string, огноо: string }
// Төрлийг гараар зааж болно
const боогдсон = боох<boolean>(true);
TypeScript параметрийн утгаас T-г дүгнэн гаргадаг (inference). боох(42) гэхэд 42 нь number тул T = number гэж автоматаар ойлгодог.
Олон төрлийн параметр
Нэгээс олон төрлийн параметр хэрэглэж болно:
// T ба U хоёр өөр төрлийн параметр
function хослуул<T, U>(эхний: T, хоёрдугаар: U): [T, U] {
return [эхний, хоёрдугаар];
}
const хос1 = хослуул("нэр", 25); // [string, number]
const хос2 = хослуул(true, ["a", "b"]); // [boolean, string[]]
// Key-Value хослол
function объект_үүсгэх<K extends string, V>(түлхүүр: K, утга: V): Record<K, V> {
return { [түлхүүр]: утга } as Record<K, V>;
}
const объект = объект_үүсгэх("нэр", "Болд");
// { нэр: "Болд" }
Generic interface
interface болон type-д мөн generic ашиглаж болно:
// Generic interface
interface Хариу<T> {
өгөгдөл: T;
амжилттай: boolean;
алдаа?: string;
}
// Дурын төрлийн өгөгдлийг боож буцаах
const хэрэглэгчийн_хариу: Хариу<{ нэр: string; нас: number }> = {
өгөгдөл: { нэр: "Болд", нас: 25 },
амжилттай: true,
};
const алдааны_хариу: Хариу<null> = {
өгөгдөл: null,
амжилттай: false,
алдаа: "Сервертэй холбогдож чадсангүй",
};
Generic-тэй болсноор Хариу interface нэг удаа бичсэн боловч хэрэглэгч, бүтээгдэхүүн, захиалга — дурын төрлийн өгөгдлийг боох боломжтой болно.
Яагаад generic хэрэгтэй вэ?
Generic хэрэглэхгүйгээр мөн any ашиглаж болох юм шиг санагдаж болно. Гэвч тэдгээр хоёрын хооронд чухал ялгаа бий:
// any ашиглавал — бүх мэдээлэл алдагдана
function анхны_any(массив: any[]): any {
return массив[0];
}
const утга = анхны_any([1, 2, 3]);
утга.toUpperCase(); // Алдааг ажиллах үед л олно!
// Generic ашиглавал — төрлийн мэдээлэл хадгалагдана
function анхны<T>(массив: T[]): T {
return массив[0];
}
const тоо = анхны([1, 2, 3]);
тоо.toUpperCase(); // Алдаа: number дээр toUpperCase байхгүй ✅
Generic нь any-ийн уян хатан байдлыг хадгалж, TypeScript-ийн төрлийн шалгалтыг алдагдуулахгүй.
Дараагийн хичээлд:
Generic функц — extends ашиглан төрлийн параметрт хязгаарлалт тавих, keyof оператор хэрхэн ажилладагыг сурна.