TypeScript Functions

 

Advanced Function Types in TypeScript

TypeScript is a powerful superset of JavaScript that adds static typing to the language. It allows developers to write more robust, maintainable, and scalable code by catching type-related errors during development. While basic type annotations are beneficial, understanding advanced function types can take your TypeScript skills to the next level.

Advanced Function Types in TypeScript

In this blog, we’ll dive deep into advanced function types in TypeScript, exploring various concepts, techniques, and use cases. We’ll cover function overloads, generics, conditional types, and mapped types, providing code examples and explanations along the way.

1. Function Overloads

1.1 Definition and Syntax

Function overloads in TypeScript allow you to define multiple function signatures for a single function. This enables TypeScript to infer and enforce different types based on the number and types of arguments passed to the function.

typescript
function foo(arg1: string): void;
function foo(arg1: number, arg2: string): void;
function foo(arg1: string | number, arg2?: string): void {
  // Function implementation
}

1.2 Working with Overloaded Functions

TypeScript will resolve the appropriate function signature based on the number and types of arguments provided during the function call. This way, you can ensure type safety while allowing different function call patterns.

typescript
foo("hello"); // Resolves to the first overload
foo(42, "world"); // Resolves to the second overload

Example: Creating an Overloaded PadLeft Function

Let’s create a function that pads a string or number with a specified character on the left side. We’ll provide overloads for both string and number arguments.

typescript
function padLeft(value: string, length: number, char: string): string;
function padLeft(value: number, length: number, char: string): string;
function padLeft(value: string | number, length: number, char: string): string {
  const stringValue = value.toString();
  if (stringValue.length >= length) {
    return stringValue;
  }
  const padding = char.repeat(length - stringValue.length);
  return padding + stringValue;
}

Now, we can use the padLeft function with different argument types:

typescript
const paddedString = padLeft("hello", 8, "-"); // Returns: "--hello"
const paddedNumber = padLeft(42, 5, "0"); // Returns: "00042"

2. Generics

2.1 Introduction to Generics

Generics are a powerful feature in TypeScript that allows you to create reusable components with type parameters. They enable you to design functions and classes that can work with a variety of data types while preserving type safety.

2.2 Using Generics with Functions

To define a generic function, use angle brackets (<>) to declare a type parameter. The type parameter can then be used as a regular type within the function body.

typescript
function identity<T>(arg: T): T {
  return arg;
}

Example: Implementing a Generic Stack

Let’s create a generic Stack class that can work with any data type.

typescript
class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

Now, we can use the Stack with various data types:

typescript
const numberStack = new Stack<number>();
numberStack.push(42);
numberStack.push(10);
const num1 = numberStack.pop(); // Returns: 10
const num2 = numberStack.pop(); // Returns: 42

const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
const str1 = stringStack.pop(); // Returns: "world"
const str2 = stringStack.pop(); // Returns: "hello"

3. Conditional Types

3.1 Understanding Conditional Types

Conditional types in TypeScript allow you to define types that depend on a condition. They are incredibly useful when you want to create flexible and dynamic types based on existing ones.

3.2 Using Conditional Types in Functions

To use a conditional type within a function, you can leverage the extends keyword to check for a certain condition and assign types accordingly.

typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

Example: Building a Conditional Type for Array Filtering

Let’s create a conditional type that filters elements from an array based on a given condition.

typescript
type FilterArray<T, U> = T extends U ? T : never;

function filter<T, U>(arr: T[], condition: U): FilterArray<T, U>[] {
  return arr.filter((item) => item === condition) as FilterArray<T, U>[];
}

Now, we can use the filter function with different data types:

typescript
const numbers = [1, 2, 3, 4, 5];
const filteredNumbers = filter(numbers, 3); // Returns: [3]

const fruits = ["apple", "banana", "orange"];
const filteredFruits = filter(fruits, "banana"); // Returns: ["banana"]

4. Mapped Types

4.1 Exploring Mapped Types

Mapped types in TypeScript allow you to create new types by transforming properties from an existing type. They are incredibly useful when you want to modify or add new properties to an existing type.

4.2 Applying Mapped Types in Functions

You can apply mapped types to function parameters or return types to transform or infer new types.

typescript
type Optional<T> = { [K in keyof T]?: T[K] };

Example: Converting Object Properties with Mapped Types

Let’s create a function that converts all properties of an object to optional using a mapped type.

typescript
function makePropertiesOptional<T>(obj: T): Optional<T> {
  return obj;
}

Now, we can convert an object’s properties to optional:

typescript
const user = { name: "John", age: 30 };
const optionalUser = makePropertiesOptional(user);
// optionalUser has the type: { name?: string; age?: number }

Conclusion

Congratulations! You’ve reached the end of this in-depth exploration of advanced function types in TypeScript. We covered function overloads, generics, conditional types, and mapped types, giving you a comprehensive understanding of these powerful features.

By leveraging advanced function types, you can write more expressive and type-safe code, making your TypeScript projects more robust and maintainable. Start applying these concepts in your projects to level up your TypeScript skills and become a more proficient developer. Happy coding!

Previously at
Flag Argentina
Argentina
time icon
GMT-3
Experienced software engineer with a passion for TypeScript and full-stack development. TypeScript advocate with extensive 5 years experience spanning startups to global brands.