Cover image

การใช้งาน Generic type ใน TypeScript

13 Aug 2023

Share to:

สวัสดีครับในบทความนี้เราจะมาเรียนรู้การใช้งาน Generic Type ใน TypeScript กันครับ

สำหรับภาษาทางด้าน Programming ที่จะต้องระบุ Type หรือชนิดของข้อมูล อย่าง C#, Java, ฯลฯ รวมไปถึง TypeScript ด้วย ในทุกๆครั้งที่เราประกาศตัวแปรขึ้นมาใช้งาน เราจําเป็นต้องระบุ Type หรือชนิดของข้อมูลทุกครั้ง

Generic Type จะมีประโยชน์ในการณีที่เราต้องการสร้าง Function, Class, Method ฯลฯ ที่เป็น Dynamic type ที่ผู้ใช้งานสามารถระบุ Type ที่จะทํางานเข้าไปตอนใช้งานได้ เพื่อให้เข้าในการทํางานมากขึ้นมาดูตัวอย่างแบบง่ายๆดังนี้

function identity(arg: number): number {
  return arg;
}

ปกติเวลาเราสร้าง Function ขึ้นมาใช้งานเราจะสร้างแบบ Code ด้านบน ซึ่งจะเห็นว่า Type ของข้อมูลที่รับเข้ามาจะถูกกําหนดเป็น number เท่านั้น หมายความว่าถ้าเราจะไม่สามารถจะนำ Function นี้ไปใช้กับ Type อื่นๆได้เลย

ถ้าเราต้องการให้ Function นี้สามารถใช้งานกับ Type อื่นๆได้ตามที่ ผู้ใช้งานระบุ เราจึงใช้ Generic Type เข้ามาช่วย โดยสามารถเขียนได้ดังนี้

function identity<T>(arg: T): T {
  return arg;
}

// สามารถใช้งานได้แบบนี้
const a = identity<string>('My string'); // a จะมี Type เป็น string
const b = identity('My string'); // b จะมี Type เป็น string
const c = identity<number>(100); // c จะมี Type เป็น number
const d = identity(100); // d จะมี Type เป็น number

จากตัวอย่างด้านบน เราสามารถใช้ "" เพื่อบอกว่าเป็น Generic Type โดยที่เราสามารถใช้ “T” ไปกำหนด Type ตามจุดต่างๆของ function ได้เลย ซึ่ง Code ด้านบน “T” ถูกกำหนดให้กับ Parameter ที่รับเข้ามา และ Type ของข้อมูลที่จะ Return ออกจาก Funvtion นี้ หมายความว่า Type ของข้อมูลที่ส่งเข้ามาทาง Parameter ของ Funvtion นี้จะต้องเป็น Type เดียวกันกับข้อมูลที่ Return ออกไป

Note: “T” สามารถเปลี่ยนเป็น Wording อื่นๆได้ ไม่จําเป็นต้องเป็น “T” เพียงอย่างเดียว

นอกเหนือจากนี้คุณสามารถเปลี่ยน “identity” Function ด้านบนไปเป็น Interface ได้ตามตัวอย่างนี้ครับ

interface GenericIdentityFn {
  <Type>(arg: Type): Type;
}

function identity<Type>(arg: Type): Type {
  return arg;
}

let myIdentity: GenericIdentityFn = identity;

Generic Class

Generic Class ก็ใช้งานคือระบุไว้ใน ”<>” เช่นเดียวกันครับ มาดูตัวอย่างกันดังนี้

class GenericNumber<NumType> {
  zeroValue: NumType;
  add: (x: NumType, y: NumType) => NumType;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};

console.log(myGenericNumber.add(myGenericNumber.zeroValue, 100));

// More Example

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = '0';
stringNumeric.add = function (x, y) {
  const numX = +x; // Convert string to number
  const numY = +y; // Convert string to number
  return numX + numY;
};

console.log(stringNumeric.add(stringNumeric.zeroValue, '100'));

จากตัวอย่างด้านบน เราสามารถสร้าง Class ที่เป็น Generic Type ซึ่งจะสามารถทํางานกับ Type อื่นๆ ได้ตามที่ผู้ใช้งาน Class กําหนด

Generic Constraints

สําหรับการทํางานบางอย่างเราอาจจะต้องการที่จะระบุลงลึกไปอีกว่า Class หรือ function ที่เราจะสร้างขึ้นมานั้นจะต้องการ Type เป็นแบบใดยกตัวอย่างเช่น ถ้าเราต้องการสร้าง Funvtion ที่เอาไว้หา Length เราก็สามารถระบุไปแบบนี้ได้ครับ

interface Lengthwise {
  length: number;
}

function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}

// ตอนใช้งานจะรับเฉพาะ Paramiter type ที่มี property เป็น "length"
loggingIdentity({ length: 10, value: 3 }); // No Error
loggingIdentity('my string'); // No Error
loggingIdentity(100); // Error เพราะว่า type number ไม่มี property length

Using Type Parameters in Generic Constraints

เราสามารใช้ Type Parameters ใน Generic Constraints ตัวอย่างเช่นถ้าเราต้องการจะให้แน่ใจว่า ผู้ที่นํา Class หรือ Function ไปใช้งานจะระบุ propery หรือค่าบางอย่างมาถูกต้อง มาดูตัวอย่างกันครับ

function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
  return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, 'a');
getProperty(x, 'm'); // Error เพราะ property "m" ไม่มีใน x

Using Class Types in Generics

เราสามารถกำหนด Class type ใน Generic ได้ ตามตัวอย่างนี้ครับ

class BeeKeeper {
  hasMask: boolean = true;
}

class ZooKeeper {
  nametag: string = 'Mikle';
}

class Animal {
  numLegs: number = 4;
}

class Bee extends Animal {
  numLegs = 6;
  keeper: BeeKeeper = new BeeKeeper();
}

class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper();
}

function createInstance<A extends Animal>(c: new () => A): A {
  return new c();
}

createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;

Copyright © 2019 - 2024 thiti.dev |  v1.19.0 |  Privacy policy |  status | 

            วงแหวนเว็บ