When it comes to using an identity function that simply returns an argument without any type restrictions, there is really no difference:
const result: any = identityFunction(['whatever']);
However, things start to differ when we introduce typed code into the mix:
const foo: string = identityFunction('ok');
const bar: string = identityFunction([{ not: 'ok' }]);
Utilizing generic types adds a layer of semantic clarity as well. For instance, this signature indicates that the function is untyped and can return anything:
function identityFunction(arg: any): any { ... }
In contrast, this signature implies that the function will return the same type as its argument:
function identityFunction<T>(arg: T): T { ... }
While the example of just returning the argument may seem simplistic, incorporating type restrictions within generics can prove advantageous (unlike with the blanket use of `any`):
function identityFunction<T>(arg: T[]): T[] {
return arg.map((value, index) => arg[index - 1]);
}
The benefits become even more pronounced when working with other generic classes and functions together, but disappear when non-generics are included:
function identityFunction<T>(arg: T[]): T[] {
return Array.from(new Set<T>(arg));
}
This approach ensures consistency in maintaining the type T
between input (argument) and output (return value):
const foo: string[] = identityFunction(['ok']);
const bar: string[] = identityFunction([{ not: 'ok' }]);
It's important to note that there won't be any performance differences since TypeScript types only exist during design time.