Functions are fundamental building blocks in JavaScript and TypeScript. TypeScript enhances functions with optional static typing, helping you catch errors early and improve code quality. This guide will walk you through the essentials of TypeScript functions, from basic syntax to best practices.
In TypeScript, you can specify the types of function parameters and return values:
Basic Function
// Function with typed parameters and return typefunctionadd(a:number, b:number):number {return a + b;}// Using the functionconstsum=add(5,3); // 8
The function add takes two parameters of type number and returns a value of type number. TypeScript ensures that you call this function with the correct parameter types.
Function expressions work similarly, but you can explicitly specify the function type:
Function Expression
// Function expression with type annotationconstmultiply: (x:number, y:number) =>number=function (x, y) {return x * y;};// Arrow function version (more concise)constdivide= (x:number, y:number):number=> x / y;
You can provide default values for parameters, which are used when the argument is omitted or undefined:
Default Parameters
functioncreateMessage(name:string, role:string="user"):string {return`${name} is a ${role}`;}createMessage("Alice"); // "Alice is a user"createMessage("Bob","administrator"); // "Bob is a administrator"
The never type represents values that never occur. It's used for functions that never return (e.g., they throw an exception or enter an infinite loop):
Never Return Type
functionthrowError(message:string):never {thrownewError(message);}functioninfiniteLoop():never {while (true) {// Do something forever }}
You can define a function type and use it for variables:
Function Types
// Define a function typetypeMathOperation= (x:number, y:number) =>number;// Functions that match this typeconstadd:MathOperation= (a, b) => a + b;constsubtract:MathOperation= (a, b) => a - b;// Function that takes a function as argumentfunctioncalculate(operation:MathOperation, a:number, b:number):number {returnoperation(a, b);}calculate(add,5,3); // 8calculate(subtract,10,4); // 6
TypeScript can infer the types of function parameters from context in many situations:
Contextual Typing
// The array's forEach method provides type contextconstnumbers= [1,2,3,4,5];// TypeScript knows 'num' is a number from contextnumbers.forEach((num) => {console.log(num.toFixed(2));});
Functions can act as type guards, which help TypeScript narrow down types:
Type Guards
functionisString(value:any): value isstring {returntypeof value ==="string";}functionprocessValue(value:string|number) {if (isString(value)) {// TypeScript knows value is a string hereconsole.log(value.toUpperCase()); } else {// TypeScript knows value is a number hereconsole.log(value.toFixed(2)); }}
Here are some examples of how to use TypeScript functions in common scenarios:
Event Handlers
Event Handlers
// Event handler function typed with TypeScriptfunctionhandleClick(event:MouseEvent):void {console.log("Button clicked at:",event.clientX,event.clientY);event.preventDefault();}// Adding an event listenerdocument.querySelector("button")?.addEventListener("click", handleClick);
Async Functions
Async Functions
// Async function with TypeScript typesasyncfunctionfetchUserData(userId:string):Promise<User> {constresponse=awaitfetch(`/api/users/${userId}`);if (!response.ok) {thrownewError(`Failed to fetch user: ${response.statusText}`); }returnresponse.json();}// Using the async functioninterfaceUser { id:string; name:string; email:string;}asyncfunctiondisplayUser(userId:string):Promise<void> {try {constuser=awaitfetchUserData(userId);console.log(`User: ${user.name} (${user.email})`); } catch (error) {console.error("Error:", error); }}
Callback Patterns
Callback Patterns
// Function with a callback parameterfunctionprocessData(data:string[],callback: (item:string, index:number) =>void):void {data.forEach((item, index) => {callback(item, index); });}// Using the function with a callbackconstitems= ["apple","banana","cherry"];processData(items, (item, index) => {console.log(`Item ${index +1}: ${item}`);});
2. Use function declarations for regular functions
Function declarations are hoisted and often more readable than function expressions for standard functions:
// Good: Function declarationfunctioncreateGreeting(name:string):string {return`Hello, ${name}!`;}// Less preferable for simple functionsconstcreateGreeting= (name:string):string=> {return`Hello, ${name}!`;};
3. Use arrow functions for callbacks and short functions
Arrow functions are more concise and maintain the this context:
// Good: Arrow function for callback[1,2,3].map((n:number):number=> n *2);// Good: Arrow function for short operationsconstdouble= (n:number):number=> n *2;
4. Prefer union types over overloads when possible
For simple cases, union types can be clearer than function overloads:
// Good: Union typefunctionformatValue(value:string|number):string {if (typeof value ==="string") {returnvalue.trim(); }returnvalue.toFixed(2);}// More complex but sometimes necessary: OverloadsfunctionformatValue(value:string):string;functionformatValue(value:number):string;functionformatValue(value:string|number):string {// Implementation}
5. Use default parameters instead of conditionals when possible
Default parameters make function signatures clearer:
// Good: Two focused functionsfunctionvalidateUser(user:User):boolean {// Only handle validation logic}functionsaveUser(user:User):void {// Only handle saving logic}// Avoid: One function doing multiple thingsfunctionvalidateAndSaveUser(user:User):boolean {// Handles both validation and saving}
7. Document functions with JSDoc comments
JSDoc comments provide additional information that TypeScript can use:
/** * Calculates the distance between two points * @param{Point} point1 - The first point * @param{Point} point2 - The second point * @returns{number} The distance between the points */functioncalculateDistance(point1:Point, point2:Point):number {constdx=point2.x -point1.x;constdy=point2.y -point1.y;returnMath.sqrt(dx * dx + dy * dy);}
Create a function called calculateTax that takes an amount (number) and a tax rate (number) and returns the tax amount. Use proper TypeScript type annotations.
Exercise 2: Function with Optional Parameters
Create a function called buildProfile that takes a person's name (required), age (required), and occupation (optional). The function should return a formatted string with the person's information.
Exercise 3: Function Types and Callbacks
Create a function processNumbers that takes an array of numbers and a callback function. The callback should receive a number and return a number. The processNumbers function should apply the callback to each number in the array and return a new array with the results.
TypeScript functions enhance JavaScript functions with static typing, making your code more robust and maintainable. Key points covered in this guide include:
Basic Syntax: TypeScript allows you to specify parameter types and return types for functions.
Optional and Default Parameters: Make parameters optional with ? or provide default values with =.
Rest Parameters: Handle variable numbers of arguments with rest parameters (...args).
Function Types: Define and reuse function type signatures for consistent typing.
Type Guards: Create functions that help TypeScript narrow down types in conditional blocks.
Best Practices:
Always specify return types
Use function declarations for regular functions
Use arrow functions for callbacks
Keep functions focused on a single responsibility
Document functions with JSDoc comments
By applying these principles, you can write TypeScript functions that are more reliable, easier to understand, and simpler to maintain.