Generic Functions
Generics allow you to create functions that can work with different types while maintaining type safety.
The syntax for defining a generic function includes type parameters that act as placeholders for the types that will be provided when the function is called.
ts
// Syntax for a generic function with different input and output typesfunctionconcatenate <X ,Y >(param1 :X ,param2 :Y ): string {return `${param1 } ${param2 }`;}
In this example:
<X, Y>
represent the types of input parameters that the function will accept.param1: X
indicates that the parameterparam1
is of typeX
.- Similarly,
param2: Y
indicates that the parameterparam1
is of typeY
. - The function returns a value of type
string
, regardless of the input and output parameters.
Let's look at another example:
ts
functionfunctionName <T >(parameter1 :T ):T {returnparameter1 ;}
- This function accepts only one type of input parameter
<T>
- Therefore the type of
parameter1
isT
as denoted by:(parameter1: T)
- We can use any valid identifier instead of
T
, butT
is most commonly used as it stands for "type". - The function also returns a value of type
T
, hence we denote:T
after the declaration and before the opening curly brace{
to specify the return type asT
.
A generic function can accept parameters of different types and return results of those types.
ts
// genericFunctions.ts// A generic function that accepts multiple elements and logs the element passed to itfunctionlogData <T >(data :T ):T {console .log (data );returndata ;}// Using the function with different typesletstringLog =logData <string>("User login successful");letnumberLog =logData <number>(42);letobjectLog =logData <{userId : number;action : string }>({userId : 123,action : "login",});// Outputs to the console// "User login successful"// 42// { userId: 123, action: 'login' }
The function can be used to log any type of data without needing multiple logging functions for different types.
But Can't I just use any
Consider this scenario:
ts
functionlogDataAny (data : any): any {// No error at compile time, Possible runtime errorconsole .log (data .toFixed ());returndata ;}// VSfunctionlogDataGeneric <T >(data :T ):T {// Warning T may not be a number and have `toFixed` methodProperty 'toFixed' does not exist on type 'T'.2339Property 'toFixed' does not exist on type 'T'.console .log (data .()); // Error here toFixed returndata ;}
If we use the any
type we lose all error-checking. It's better to use the generic because Typescript will warn us before we use any methods or properties that may not exist at runtime.
It's better to find these errors and run checks at compile time, rather than find out about them at runtime.