functions-arrow-functions-and-overloads
Functions: Arrow Functions and Overloads in TypeScript:
Let's create TypeScript examples for the given concepts:
Understanding Arrow Functions:
Arrow functions provide a concise syntax for writing function expressions. They do not bind their own this
, arguments
, super
, or new.target
, which makes them ideal for use cases like event handlers and callbacks, where you want this
to refer to the enclosing context.
const logMessage = (message: string) => console.log(message);
logMessage('Hello, TypeScript!');
Lexical Scoping:
The this
inside an arrow function is lexically or statically bound to the context of where the function is defined, not where it is used. This is useful when you have callback functions in a class method and you want this
to refer to the class instance.
class Logger {
message = 'Logger message';
log = () => {
console.log(this.message);
};
}
const logger = new Logger();
setTimeout(logger.log, 1000); // logs 'Logger message' after 1 second
Function Overloading:
TypeScript allows for function overloading by writing multiple function signatures before the function implementation. This can help document multiple ways of calling the function with different parameter types.
function greet(name: string): string;
function greet(age: number): string;
function greet(value: string | number): string {
if (typeof value === 'string') {
return `Hello, ${value}`;
} else {
return `You are ${value} years old.`;
}
}
console.log(greet('Alice')); // "Hello, Alice"
console.log(greet(42)); // "You are 42 years old."
Parameter Types:
TypeScript allows you to enforce types for the parameters of a function, ensuring that the correct type of arguments is passed.
function multiply(a: number, b: number): number {
return a * b;
}
multiply(5, 2); // 10
// multiply('5', '2'); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
Return Types:
Defining the return type of a function can help with maintainability and readability of the code, as well as catch bugs early in the development process.
function getFullName(firstName: string, lastName: string): string {
return firstName + ' ' + lastName;
}
// const wrongName: number = getFullName('John', 'Doe'); // Error: Type 'string' is not assignable to type 'number'.
Optional Parameters:
In TypeScript, parameters can be made optional by appending a ?
to their names. These parameters can be left undefined when calling the function.
function buildName(firstName: string, lastName?: string): string {
if (lastName) return firstName + ' ' + lastName;
else return firstName;
}
buildName('Bob'); // "Bob"
buildName('Bob', 'Adams'); // "Bob Adams"
Default Parameters:
You can set default values for parameters in TypeScript, which will be used if no value or undefined
is passed.
function applyDiscount(price: number, discount: number = 0.05): number {
return price * (1 - discount);
}
applyDiscount(100); // 95
applyDiscount(100, 0.1); // 90
Rest Parameters:
TypeScript provides rest parameters syntax that allows you to represent an indefinite number of arguments as an array.
function getTotal(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0);
}
getTotal(10, 20, 30); // 60
Void Type:
A void
return type is used when a function has no return value. It indicates not to expect any value in return.
function warnUser(): void {
console.log('This is a warning message!');
// return 'something'; // Error: Type 'string' is not assignable to type 'void'.
}
warnUser();
These examples should illustrate the key points clearly and are concise enough to be easily understood.
Function Type Declarations:
You can declare a function type that enforces the function signature, improving type safety.
type GreetFunction = (name: string) => string;
const greet: GreetFunction = (name) => `Hello, ${name}`;
This Keyword in Functions:
The
this
keyword can be explicitly typed in function signatures to ensure correct usage ofthis
.
function greet(this: {name: string}) {
return `Hello, ${this.name}`;
}
const user = {name: 'Alice', greet};
console.log(user.greet()); // Correct usage
Generics in Functions:
Generics allow you to write functions that work on a variety of types while still maintaining type information.
function firstElement<Type>(arr: Type[]): Type | undefined {
return arr[0];
}
let numbers = firstElement([1, 2, 3]); // numbers is inferred to be a number
let strings = firstElement(["hello", "world"]); // strings is inferred to be a string
Async Functions:
TypeScript supports asynchronous functions using the
async
andawait
keywords, simplifying asynchronous code.
async function fetchData(url: string): Promise<string> {
const response = await fetch(url);
const data = await response.text();
return data;
}
Callback Functions:
A callback function is passed as an argument to another function and is executed after the completion of that function.
function processArray(items: string[], callback: (item: string) => void) {
items.forEach(callback);
}
processArray(['a', 'b', 'c'], (item) => console.log(item));
Higher-Order Functions:
Functions that take other functions as parameters or return functions are called higher-order functions.
function withLogging(fn: () => void): () => void {
return () => {
console.log('Function started');
fn();
console.log('Function finished');
};
}
const loggedFunction = withLogging(() => console.log('Hello'));
loggedFunction();
Anonymous Functions:
Functions without a name are called anonymous functions. They are often used as arguments to higher-order functions.
const names = ['Alice', 'Bob', 'Charlie'];
names.forEach(function (name) {
console.log(name);
});
IIFE (Immediately Invoked Function Expressions):
IIFEs are functions that are defined and executed immediately after creation, often used to create a new variable scope.
(function() {
const temp = 'This is an IIFE';
console.log(temp);
})();
Closures:
Closures allow a function to access variables from an enclosing scope even after the outer function has completed.
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
const counter = makeCounter();
console.log(counter()); // 0
console.log(counter()); // 1
Function Overload Resolution:
When multiple function overloads are available, TypeScript chooses the first matching overload based on the provided arguments.
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
const sum = add(1, 2); // Correctly infers to number overload
const concatenated = add("hello", "world"); // Correctly infers to string overload
Function Overload Limitations:
There are limitations to function overloading, such as not being able to specify different return types for different overloads.
function add(a: number, b: number): number;
function add(a: number, b: number, c: number): number; // Error: A function overload must have a unique signature
function add(a: any, b: any, c?: any): any {
if (c !== undefined) {
return a + b + c;
}
return a + b;
}