TypeScript is an open source language based on JavaScript, one of the most popular and used languages. Typescript extends Javascript by adding some static types.
Types provides a more explicit method for describing object morphology, better documentation description, through which TypeScript can determine that our code is working correctly. In this article, we will learn about Generics in TypeScript.
What are generics?
Generics are a feature in TypeScript and other programming languages that allow us to write a common function, class, or interface for many different data types, and only specify the specific data type when using the data type. there.
Syntax Generics
Generics in TypeScript inside curly braces, in the format <T>
, where T represents the type passed in. <T>
can be read in a generic form of type T.
In this case, T will behave the same way parameters work in functions, as placeholders for a type that will be declared when an instance of the struct is created. Therefore, generics types specified inside curly braces are also known as generic type parameters or just type parameters. Multiple generic types can also appear in a definition, such as <T, K, A>
.
For example:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">identity</span> <span class="token generic class-name"><span class="token operator"><</span> <span class="token constant">T</span> <span class="token operator">></span></span></span> <span class="token punctuation">(</span> arg <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> arg <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> output1 <span class="token operator">=</span> <span class="token generic-function"><span class="token function">identity</span> <span class="token generic class-name"><span class="token operator"><</span> <span class="token builtin">string</span> <span class="token operator">></span></span></span> <span class="token punctuation">(</span> <span class="token string">"myString"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// type of output will be 'string'</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> output1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> output2 <span class="token operator">=</span> <span class="token generic-function"><span class="token function">identity</span> <span class="token generic class-name"><span class="token operator"><</span> <span class="token builtin">number</span> <span class="token operator">></span></span></span> <span class="token punctuation">(</span> <span class="token number">123</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// type of output will be 'number'</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> output2 <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Create a new instance of generics class
To create an instance of a generic class, we need to construct the class and tell the compiler through the <>
syntax. We can use any type for type T in generic syntax, including base types, classes or interfaces.
For example:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">class</span> <span class="token class-name">GenericClass <span class="token operator"><</span> <span class="token constant">T</span> <span class="token operator">></span></span> <span class="token punctuation">{</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">;</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token operator">=</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> instance1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericClass <span class="token operator"><</span> <span class="token builtin">number</span> <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token number">123</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> instance1 <span class="token punctuation">.</span> field <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Output: 123, type of output will be 'number'</span> |
In the example above. we have created a generic class with a data type parameter T. When creating an instance of this class we need to specify the specific data type we want to use (in this case number
)
Limit type T
When using generics, it is sometimes advisable to restrict type T to only a specific type, or subset of types. In these cases, we don’t want our generic code to be available for any type of object, we just want it for a specific subset of objects. TypeScript uses inheritance to do this with generics.
Example 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">Shape</span> <span class="token punctuation">{</span> width <span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">;</span> height <span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">GenericClass <span class="token operator"><</span> <span class="token constant">T</span> <span class="token keyword">extends</span> Shape <span class="token operator">></span></span> <span class="token punctuation">{</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">;</span> <span class="token function">setField</span> <span class="token punctuation">(</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token operator">=</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericClass <span class="token operator"><</span> Shape <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> instance <span class="token punctuation">.</span> <span class="token function">setField</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> width <span class="token operator">:</span> <span class="token number">100</span> <span class="token punctuation">,</span> height <span class="token operator">:</span> <span class="token number">200</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> instance <span class="token punctuation">.</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Output: { width: 100, height: 200 }</span> |
In the above example, we have created a generic class GenericClass
with type T that inherits from Shape class. This means that only instances of Shape class or classes that inherit from Shape are allowed to pass this generic class
Example 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">class</span> <span class="token class-name">GenericClass <span class="token operator"><</span> <span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token builtin">number</span> <span class="token operator">|</span> <span class="token builtin">string</span> <span class="token operator">></span></span> <span class="token punctuation">{</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">;</span> <span class="token function">setField</span> <span class="token punctuation">(</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token operator">=</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericClass <span class="token operator"><</span> <span class="token builtin">string</span> <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> instance <span class="token punctuation">.</span> <span class="token function">setField</span> <span class="token punctuation">(</span> <span class="token string">"Hello World"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> instance <span class="token punctuation">.</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Output: "Hello World"</span> |
In example 2 above, we also use extends
to specify that T can only be number
or string
. This means that when creating an instance of the class, we can only specify the data type as number
or string
.
Generic interfaces
We can also use interfaces with generic type syntax.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">interface</span> <span class="token class-name">GenericInterface <span class="token operator"><</span> <span class="token constant">T</span> <span class="token operator">></span></span> <span class="token punctuation">{</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">;</span> <span class="token function">setField</span> <span class="token punctuation">(</span> field <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">;</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">ImplementationClass</span> <span class="token keyword">implements</span> <span class="token class-name">GenericInterface <span class="token operator"><</span> <span class="token builtin">number</span> <span class="token operator">></span></span> <span class="token punctuation">{</span> field <span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">;</span> <span class="token function">setField</span> <span class="token punctuation">(</span> field <span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token operator">=</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> field <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ImplementationClass</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> instance <span class="token punctuation">.</span> <span class="token function">setField</span> <span class="token punctuation">(</span> <span class="token number">123</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> instance <span class="token punctuation">.</span> <span class="token function">getField</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Output: 123</span> |
In this example we have created a generic interface GenericInterface
with type T. Then create a class ImplementationClass
that implements interface GenericInterface
and specify string
as type T. When using class ImplementationClass
, we have to comply with the requirements of generic interface
, which includes specific properties, methods, and data types.
Creating new objects in generics
Sometimes, generic classes may need to create an object of the type passed in as type T.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">class</span> <span class="token class-name">FirstClass</span> <span class="token punctuation">{</span> id <span class="token operator">:</span> <span class="token builtin">number</span> <span class="token operator">=</span> <span class="token number">10</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">SecondClass</span> <span class="token punctuation">{</span> name <span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">=</span> <span class="token string">'my name'</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">GenericCreator <span class="token operator"><</span> <span class="token constant">T</span> <span class="token operator">></span></span> <span class="token punctuation">{</span> <span class="token function">create</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token constant">T</span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> creator1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericCreator <span class="token operator"><</span> FirstClass <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> firstClass <span class="token operator">:</span> FirstClass <span class="token operator">=</span> creator1 <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> creator2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericCreator <span class="token operator"><</span> SecondClass <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> secondClass <span class="token operator">:</span> SecondClass <span class="token operator">=</span> creator2 <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
In the above example, we define 2 classes: FirstClass, SecondClass. Then we have a generic class and a function create. Create this function to create a new instance of type T. In the last 4 lines of the example we want to use GenericCreator
class to create a new instance.
When we run the above example, we will generate a TypeScript compile error
According to the TypeScript documentation, to allow a generic class to create objects of type T, we need to refer to type T by its constructor. So in the above example create function
needs to be rewritten as follows:
1 2 3 4 5 6 | <span class="token keyword">class</span> <span class="token class-name">GenericCreator <span class="token operator"><</span> <span class="token constant">T</span> <span class="token operator">></span></span> <span class="token punctuation">{</span> <span class="token function">create</span> <span class="token punctuation">(</span> c <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token keyword">new</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">c</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
We will divide create function
into its constituent parts. The first is an argument passed, named c
. This argument is defined as of type {new(): T}
. This is a way that allows us to refer to T by its constructor. Then we define a new anonymous type overloads new() function
to have a constructor that returns type T. The purpose of this function is simply to return a new instance of the variable c.
After rewriting create function
helps them to get rid of the precompile error. However, for this change we have to pass the class definition to create function
, like so:
1 2 3 4 5 6 7 8 | <span class="token keyword">var</span> creator1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericCreator <span class="token operator"><</span> FirstClass <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> firstClass <span class="token operator">:</span> FirstClass <span class="token operator">=</span> creator1 <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> FirstClass <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> firstClass <span class="token punctuation">.</span> id <span class="token punctuation">)</span> <span class="token comment">// output: 10</span> <span class="token keyword">var</span> creator2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GenericCreator <span class="token operator"><</span> SecondClass <span class="token operator">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">var</span> secondClass <span class="token operator">:</span> SecondClass <span class="token operator">=</span> creator2 <span class="token punctuation">.</span> <span class="token function">create</span> <span class="token punctuation">(</span> SecondClass <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token builtin">console</span> <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> secondClass <span class="token punctuation">.</span> name <span class="token punctuation">)</span> <span class="token comment">// output: "my name"</span> |
any and generics types
Both any
and generics
are ways to specify data types in TypeScript. However, these two ways have some important differences.
any
:any
is a generic type, meaning any value can be assigned to a variable or parameter of typeany
. This leaves us with no control over data type or data integrity, so usingany
can lead to some bugs during development.generics
:Generics
are a way to specify a generic type for a class, interface, or function. Generics type allows us to specify which specific object should have a set of properties or methods and restrict the data type we want to use. This allows us to have control over the data type and data integrity, helping to avoid errors during development.
So, if we want to control the data type and reduce error in our code, we should use generics
instead of any
.
Conclude
In this article, we will learn about Generics type
in TypeScript. In the following article, we will learn about mixins
pattern using generics type with inheritance. Thank you for following this post
References
- Mastering TypeScript – Second Edition
- https://www.typescriptlang.org/
- ChatGPT