Mastering TypeScript Generics: From Arrays to Arrow Functions with Type Safety

Mastering Generics with Arrays and Arrow Functions in TypeScript

Example 1: Generics in Arrays

// Without Generics
function getFirstItem(arr: (string | number)[]): (string | number) {
    return arr[0];
}

let arr1 = getFirstItem([1, 2, 3]);         // 1
let arr2 = getFirstItem(['one', 'two']);   // 'one'

let str = arr2.toLowerCase();    // Error: Property 'toLowerCase' does not exist on type 'string | number'.

Solution using Generics:

function getFirstItem<Type>(arr: Type[]): Type {
    return arr[0];
}

let arr1 = getFirstItem([1, 2, 3]);         // 1
let arr2 = getFirstItem(['one', 'two']);   // 'one'

let str = arr2.toUpperCase();    // 'ONE'

Now, with generics, TypeScript infers the type correctly, allowing operations like toUpperCase() on strings.

Example 2: Using Arrow Functions

const getFirstItem = <Type, >(arr: Type[]): Type => {
    return arr[0];
}

let arr1 = getFirstItem([1, 2, 3]);   // 1

The use of generics with arrow functions is concise and maintains type safety.

The , in <Type,> denotes that this is not JSX syntax but rather a generic type parameter.

Generics provide a flexible and powerful way to work with different data types while maintaining type safety in TypeScript.

Unlocking Type Safety with Generic Constraints in TypeScript

Example 1: Type Parameter Constraints

function printName<Type extends { name: string }>(obj: Type): void {
  console.log(obj.name);
}

printName({ name: "John" });  // Valid
// printName({ age: 25 });      // Error: Type '{ age: number }' is not assignable to a parameter of type '{ name: string }'.

Explanation:

  • The generic function printName has a type parameter Type with a constraint { name: string }.

  • This ensures that any object passed to printName must have a name property of type string.

  • Calling printName({ name: "John" }) is valid, but attempting to pass an object without a name property results in a compile-time error.

Example 2: Multiple Type Parameters with Constraints

function display<T, U extends number>(valOne: T, valTwo: U): object {
  return { valOne, valTwo };
}

display(3, 2);    // { valOne: 3, valTwo: 2 }
// display(3, "a");  // Error: Argument of type 'string' is not assignable to parameter of type 'number'.

Explanation:

  • The function display has two type parameters, T and U, with a constraint on U that it must extend the number type.

  • This ensures that valTwo must be a number.

  • The first call display(3, 2) is valid, but the second call display(3, "a") results in a type error as a string is not assignable to a parameter of type number.

Type parameter constraints enhance type safety by enforcing specific conditions on generic types.

Stay tuned for more TypeScript insights and practical tips!