has taken the development world by storm and has been adopted by most developers and startups across the globe. Typescript Loosely explained as a stricter, more syntactical superset of , it has become quite popular for a number of reasons, some of which are: JavaScript It makes our code easier to read and understand. It saves us from running into painful bugs, as we know what value and type are entering our function and what value and type we are expected to receive. To help us get more acquainted with the programming language, we’ll explain what typescript generics are and how to implement them in our projects (with examples where they would come in handy). Table of Contents What are Typescript Generics? Why use them? Working with Typescript Generics on Arrays Interface Classes Typescript Generics Constraints Conclusion Resources What are Typescript Generics? are a way to define flexible variables, functions, objects, arrays etc. Sometimes we need a particular function or object to work with a wide range of types and not be restricted by the rigidity that comes with writing with typescript. That's where typescript generics come in. Typescript Generics Why use them? What if we wanted to define a function that gets the user's id, and we are not sure if the id will be a string or an integer? What do we do then? We could either define multiple functions to take care of the different types or use the keyword like the code block below. any function getId(arg: any): any { returns arg; } let id1 = getId('Amara') The above function would work in our project and not throw off any error; however, we trade-off the type definition for flexibility by using the type. When an argument is passed in, we have no idea what the type of the argument is and what type the function returns. any These are all the issues that typescript generics solve. function getId<Type>(arg: Type): Type { return arg; } let userOne = getId<String>('identifier1') We add a variable, then pass as the type of the argument, and finally, specify that the function will return a value of . Type Type Type This means that the argument’s type is also the value type the function returns. NOTE: Type is just a placeholder and not the standard, as we can replace it with any other string, e.g. <T>, <ReturnType> etc. Creating a function, we then explicitly state that the type of the argument is a string and that the function should also return a string. userOne When we hover over we can see that we get a type of string, and that is due to the magic of typescript generics. userOne Working with Typescript Generics on Arrays function getLastItem<Type>(arr: Type ): Type{ return arr[arr.length -1] } let arr1 = getLastItem([1,2,3,4,5]) console.log(arr1) When we run this program, we get an error because the function is flexible but doesn’t know the type of argument we are looking to pass in and therefore doesn’t know that it has a length property. We can resolve this by specifying that we want a generic array type. function getLastItem<Type>(arr: Type[] ): Type{ return arr[arr.length -1] } let arr1 = getLastItem([1,2,3,4,5]) console.log(arr1)// returns 5 The specifies that the argument will be an array typescript, knowing that there usually is a length property on the array allows us to do this. [] Interface define the behaviors of an object. Interfaces interface Person<T,U>{ (firstname: T, age: U ) : void; }; function introducePerson<T, U>(firstname: T, age: U): void{ console.log('Hey! My name is ' + firstname + ',and I am ' + age); } let person1: Person<string, number> = introducePerson; person1("Amara", 12); // Output: Hey! My name is Amara, and I am 12 In the code block above, we declared a generic interface called with two properties, and . These properties accept generic arguments that we represent with the type variables and . Person firstname age <T> <U> With this generic interface, we can then use any method with the same signature as the interface, such as the method. introducePerson Creating the object, we state that the object has a type of the interface, signifying that will have a and property. We then define the argument types getting passed in (string and number). Equating it to the allows us to have access and eventually run the function. person1 Person person1 firstname age introducePerson Classes are explained as blueprints or templates used to create objects. Classes class Collection{ items: Array<number> = []; add(item: number) { this.items.push(item); }; remove(item: number){ const index = this.items.findIndex(i=> i === item); this.items.splice(index, 1); return this.items } } const myCollection = new Collection(); myCollection.add(1); myCollection.remove(1); In the example above, we created a class called . This class contains an array and two functions, and , to add and remove an item from the array. Collection items add remove items We then create an instance from the class , and finally, call the and method. Collection myCollection add remove This is great, right? However, this class and any instance created from this class can only work with the number type, and anything else will throw an error. class Collection<T>{ items: Array<T> = []; add(item: T) { this.items.push(item); }; remove(item: T){ const index = this.items.findIndex(i=> i === item); this.items.splice(index, 1); return this.items } } const myCollection = new Collection<number>(); myCollection.add(1); myCollection.remove(1); In the block of code above, we use generic types when creating the class , so now create different instances that work with different types. Collection Typescript Generics Constraints When working with generics in typescript, we might want more specificity than just the type of argument a function is looking to take in. With typescript generics constraints, we can decide to reject an argument not just because it is not an object but also because the object does not have specific keys or parameters in it. function addAge <T extends {name: string}>(obj: T) { let age = 40; return {... obj, age}; } let docOne = addAge({name: 'Amara',color: 'red'}); let docTwo = addAge({series: 'killing eve',color: 'red' }); // throws an error console.log(docOne); In the block of code above, we use the keyword to specify that we not only need the argument to be of an object type but that the object needs to have a property. If we replaced the name property from the argument passed into , we would have an error. extends name docOne We could also add constraints using interfaces. Recall an earlier example where we tried to find the last number in the array using the property but got an error. We then worked around it by explicitly stating that we only accepted array arguments. .length With generics constraints, we could decide not to be limited to only arrays but accept any argument as it possesses the length property. interface LengthOfArg { length: number; } function logLength<Type extends LengthOfArg>(arg: Type): Type { console.log(arg.length); return arg; } logLength({length: 52, name: 'Amara'})// 52 logLength([1,2,3])//3 logLength('Orange')// 6 logLength(2)// throws off an error In the block of code above, we use the keyword to specify that we want our argument to have the property on the interface . Doing this allows us to pass in an object containing a length property, an array, or a word and have our function not throw an error, this is because they all have a length property on them. extends length LengthOfArg Conclusion This article discussed typescript generics, why we would need them in our code, and, more importantly, how to implement them. Resources You may find the following resources helpful. Generics Typescript Playground Type Assertion