Typescript Generics
If you’re learning TypeScript you’ve probably came across Generics. Generics are a way to pass types just like you would pass parameters. The benefits of using Generics is that you can make reusable components that accept different types and are therefore also type safe. Generics are not the same as the any
type since you will be telling TypeScript what type your Generic is when you pass it in.
First let’s declare an object that we want to use:
interface DogBreed { averageHeight: number[]; isGoodDoggo: boolean; name: string; } // An object that satisfies the DogBreed type const maltese: DogBreed = { averageHeight: [7, 8, 9], isGoodDoggo: true, name: "Maltese", };
Now let’s make a class that uses the DogBreed
type:
// A class that accepts dogs and keeps track of them class DogKennel { dogs: DogBreed[] = []; // A function within DogKennel that accepts an object of the type DogBreed and adds it to the dogs array admitDog(newDog: DogBreed) { this.dogs.push(newDog); } // A function within DogKennel that logs the dogs array to the console getAllDogs() { if (!this.dogs.length) { console.log("The Kennel currently has no dogs."); return; } console.log("The Kennel currently has the following dogs:"); console.log(this.dogs); } }
With the DogKennel
class we can create as many DogKennels as we want and admit some dogs!
const PetStoreKennel = new DogKennel(); PetStoreKennel.admitDog(maltese); PetStoreKennel.admitDog(maltese); PetStoreKennel.getAllDogs(); const VeterinarianKennel = new DogKennel(); VeterinarianKennel.getAllDogs(); const FosterHomeKennel = new DogKennel(); FosterHomeKennel.admitDog(maltese); FosterHomeKennel.getAllDogs();
This is great if all we had in the world were dogs. But what about these animals?
// An object that satisfies the CatBreed type const tabbyCat: CatBreed = { isKindOfMean: true, mostRecentlyDestroyedObject: "couch", name: "Tabby Cat", }; // An object that satisfies the ReptileBreed type const redFootedTortoise: ReptileBreed = { diet: ["vegetables", "fruit", "hay", "berries"], lifeSpan: 50, name: "Tortoise", }; // An object that satisfies the BirdBreed type const cockatiel: BirdBreed = { canWhistle: true, name: "Pretty Bird", primaryColor: "#d2e75f", };
How can we rewrite DogKennel
to accept these other animals? With Generics we can pass the type we want the class to accept.
class Kennel<SomeAnimalBreed> { animals: SomeAnimalBreed[] = []; admitAnimal(newAnimal: SomeAnimalBreed) { this.animals.push(newAnimal); } getAllAnimals() { if (!this.animals.length) { console.log("The Kennel currently has no animals."); return; } console.log("The Kennel currently has the following animals:"); console.log(this.animals); } } const PetStoreKennel = new Kennel<DogBreed | CatBreed | ReptileBreed | BirdBreed>(); PetStoreKennel.admitAnimal(maltese); PetStoreKennel.admitAnimal(tabbyCat); PetStoreKennel.admitAnimal(redFootedTortoise); PetStoreKennel.admitAnimal(cockatiel);
This allows us to add type safety to our classes. So if we tried to add a non-animal to Kennel
we would get an error:
// An object that satisfies the type Vehicle const camry: Vehicle = { make: "Toyota", model: "Camry", numberOfWheels: 4, }; // This causes an error: // Argument of type 'Vehicle' is not assignable to parameter of type 'DogBreed | CatBreed | ReptileBreed | BirdBreed'. PetStoreKennel.admitAnimal(camry);
Because SomeAnimalBreed
is just a name for a type we can replace it like this:
class Kennel<Type> { animals: Type[] = []; admitAnimal(newAnimal: Type) { this.animals.push(newAnimal); } ...
But often you’ll see it simplified even further with a T
:
class Kennel<T> { animals: T[] = []; admitAnimal(newAnimal: T) { this.animals.push(newAnimal); } ...
You can simplify things further by extending the different Breed interfaces to something even more generalized like Animal
type and pass that as the Generic to Kennel
like this: const PetStoreKennel = new Kennel<Animal>();
Read my Beginner Blog on TypeScript:
https://echobind.com/post/converting-javascript-to-type-script-with-react-native
Read more about Generics:
https://www.digitalocean.com/community/tutorials/how-to-use-generics-in-typescript
https://www.typescriptlang.org/docs/handbook/2/generics.html