Generics
Generics are used to create variable types for classes, functions, and type aliases. They help create reusable codes.
Generic Types
Generics for type aliases store the type as a value. Let's see an example of a generic type:
type Movie<G> = {
name: G,
quotes: G[]};
let movie1: Movie<string> = {
name: 'Inside Out',
quotes: ['They Came To Help… Because Of Sadness.', 'Get Lost In There!']};
Another example of the generic type:
type Info<S, R> = {name: S, age: R}
let info1: Info<string, number> = {name:"bella", age: 34}
console.log(info1.name)
The generic type above (type Info) has two types: S and R. info1 variable passes a string type for S and a number type for R. Otherwise, you will get an error.
Generic Functions
function Greeting<C>(name: C): C{
return name}
console.log(Greeting("Jack"))
C is the type used for the generic function. You should have the same type for both parameters and the result. The value (for the name) has a string type in the example above. However, it could be any type. If you want to get a result with a specific type, you should specify it:
function Greeting2<C>(name: C): string{
return `Hello ${name}`}
console.log(Greeting2("John"))
If the type string for the result wasn't specified in the example above, the function Greeting2 would fail because the function returns a string. You can still pass any type for the parameter. For example, it can be a number. However, the type (C - in the example above) for the parameter and the result should be the same as the previous example.
Generic Classes
Generic classes are similar to generics with type aliases and JavaScript classes. Let's see a generic class example:
class Team<T, R> {
constructor(public name: T, public people: R) {}}
let team1 = new Team("Eagles", 20);
console.log(team1)
Generic Constraints
Generics help us to create reusable codes and generic constraints allow us to limit the capabilities of types.
Generic constraint example 1
interface Cats {
name: string;
age: number}
function showCatDetails<C extends Cats> (cat1: C){
console.log(cat1.name, cat1.age)}
let cat1 : Cats = { name: "Nana", age: 1 };
showCatDetails(cat1)
The result is "Nana 1". The function showCatDetails uses the Cats interface in the example above. The name parameter should be a string and the age parameter should be a number. If they are not, there will be a type error.
Generic constraint example 2
interface Cats2 {
name: string;
age: number}
You can use type parameters in generic constraints:
function showCatDetails2<C extends Cats2, K extends keyof C>
(cat: C, name: K): C[K]{
return cat[name];}
let cat2 : Cats2 = {name: "Speedy", age: 2}
let catName : string = showCatDetails2(cat2, "name")
console.log(`Its name is ${catName}`)
Generic constraint example 3
interface Dog{
name: string;
age: number}
class Details implements Dog{
constructor(public name: string, public age: number){}
getInfo(): string {
return this.name; }}
To call the getInfo function, you need to extend the Details class and declare the function:
function getInfo<T extends Details> (dog: T): void {
console.log(`${dog.getInfo()} is ${dog.age}`)}
let dog1 = new Details("Brownie", 10)
getInfo(dog1)