TypeScript: TypeScript Function Types
Last updated: 2026-08-26
Functions are a core construct of JavaScript, and TypeScript adds a comprehensive type system to function parameters and return values—ensuring that a function’s “inputs” and “outputs” are clearly defined.
1. Function Type Annotations
(1) Parameter and Return Value Types
function add(a: number, b: number): number {
return a + b;
}
let result = add(1, 2); // result Inferred as number
// add("1", "2"); // ❌ Parameter types do not match
// add(1); // ❌ Insufficient number of parameters
(2) Return Type Inference
TypeScript can infer the return type based on the return statement, but it is recommended to explicitly specify it:
// Return Type Inference——Can be inferred, but not recommended(It's easy to make mistakes when deriving large functions)
function multiply(a: number, b: number) {
return a * b; // Inference Return number
}
// Explicit Annotation——Recommendations(Purpose of the Document + Compile-time errors)
function divide(a: number, b: number): number {
if (b === 0) throw new Error("The divisor cannot be zero.");
return a / b;
}
(3) void return type
Use void when a function has no return value:
function log(message: string): void {
console.log(message);
// None return Statement,Or return; —— All of them void
}
(4) never returns a value
Use never when a function will never return a value—it will either throw an exception or enter an infinite loop:
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) { }
}
| Return Type | Meaning |
|---|---|
void |
The function completed normally, but did not return a value |
never |
The function never ends (throws an exception or enters an infinite loop) |
▶ Example: Typed utility functions
function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
function formatCurrency(amount: number, symbol: string = "¥"): string {
return symbol + amount.toFixed(2);
}
console.log(clamp(150, 0, 100)); // 100
console.log(clamp(-5, 0, 100)); // 0
console.log(clamp(50, 0, 100)); // 50
console.log(formatCurrency(99.5)); // ¥99.50
console.log(formatCurrency(42, "$")); // $42.00
Output:
100
0
50
¥99.50
$42.00
2. Optional Parameters and Default Parameters
(1) Optional parameter ?
function greet(name: string, title?: string): string {
if (title) {
return `${title} ${name}`;
}
return `Hello,${name}`;
}
console.log(greet("Charlie")); // "Hello,Charlie"
console.log(greet("Charlie", "Engineer")); // "Engineer Charlie"
function greet(title?: string, name: string) will result in an error.
(2) Default Parameters
function createUser(name: string, role: string = "viewer", active: boolean = true): string {
return `${name},Characters:${role},${active ? "Active" : "Not activated"}`;
}
console.log(createUser("Charlie")); // Charlie,Characters:viewer,Active
console.log(createUser("Diana", "editor")); // Diana,Characters:editor,Active
console.log(createUser("Eric", "admin", false)); // Eric,Characters:admin,Not activated
(3) Optional Parameters vs. Default Parameters
| Feature | Optional Parameter title?: string |
Default Parameter role: string = "viewer" |
|---|---|---|
| When called | Optional; value is undefined | Optional; value is the default |
| Type | string | undefined |
string (not the undefined union) |
| Recommendation | The value may indeed be undefined | In most cases—it’s better to have a reasonable default value |
3. Remaining Parameters
Use the ... syntax to collect the remaining parameters into an array:
function sum(first: number, ...rest: number[]): number {
return rest.reduce((total, n) => total + n, first);
}
console.log(sum(1)); // 1
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4)); // 10
(1) The remaining parameters must be of array type
function logAll(prefix: string, ...messages: string[]): void {
messages.forEach(msg => console.log(`${prefix}: ${msg}`));
}
logAll("DEBUG", "Start the service", "Connect to the Database", "Ready");
Output:
DEBUG: Start the service
DEBUG: Connect to the Database
DEBUG: Ready
4. Function-Type Expressions
Functions can also be passed by value—in which case, a function type expression is used to specify the type:
(1) Type aliases define function types
type MathOperation = (a: number, b: number) => number;
let add: MathOperation = (a, b) => a + b;
let subtract: MathOperation = (a, b) => a - b;
let multiply: MathOperation = (a, b) => a * b;
console.log(add(10, 5)); // 15
console.log(subtract(10, 5)); // 5
console.log(multiply(10, 5)); // 50
(2) Callback Function Types
The most common use of function types—describing the signature of a callback function:
function fetchData(url: string, onSuccess: (data: string) => void, onError: (error: Error) => void): void {
// Simulating Asynchronous Operations
if (url.startsWith("https://")) {
onSuccess("Data loaded successfully");
} else {
onError(new Error("Supports only HTTPS"));
}
}
fetchData(
"https://api.example.com",
data => console.log(data), // ✅ (data: string) => void
error => console.log(error.message) // ✅ (error: Error) => void
);
(3) Defining Function Types Using Interfaces
interface Comparator {
(a: number, b: number): number;
}
let ascending: Comparator = (a, b) => a - b;
let descending: Comparator = (a, b) => b - a;
let nums = [3, 1, 4, 1, 5];
console.log([...nums].sort(ascending)); // [1, 1, 3, 4, 5]
console.log([...nums].sort(descending)); // [5, 4, 3, 1, 1]
5. Function Overloading
Function overloading allows a single function name to support different combinations of argument types—TypeScript’s overloading follows a “declaration + implementation” pattern:
(1) Basic Syntax
// Overloaded Signatures——Describe the return type for each parameter combination
function format(value: number): string;
function format(value: string): string;
function format(value: Date): string;
// Implementing Signatures——Must be compatible with all overloaded signatures
function format(value: number | string | Date): string {
if (typeof value === "number") {
return value.toFixed(2);
} else if (typeof value === "string") {
return value.trim();
} else {
return value.toISOString();
}
}
console.log(format(3.14)); // "3.14"
console.log(format(" hello ")); // "hello"
console.log(format(new Date())); // "2024-..."
(2) Overloading Order
TypeScript matches overloaded methods in the order they are declared, from top to bottom—so more specific overloads should be placed first:
// ✅ Correct Order——The specifics come first
function process(value: string): string;
function process(value: any): unknown;
// ❌ Incorrect Order——any Matches all parameters,The overloaded version that comes later will never be called.
// function process(value: any): unknown;
// function process(value: string): string;
▶ Example: Overloading to Implement Type-Safe Event Handling
function on(event: "click", handler: (x: number, y: number) => void): void;
function on(event: "keydown", handler: (key: string) => void): void;
function on(event: string, handler: Function): void {
console.log(`Registration Event:${event}`);
// Implementation details omitted...
}
// Automatically determines the correct parameter types upon invocation
on("click", (x, y) => {
console.log(`Click here:(${x}, ${y})`); // x, y Inferred as number
});
on("keydown", (key) => {
console.log(`Press the button:${key}`); // key Inferred as string
});
Output:
Registration Event:click
Registration Event:keydown
6. Types of Arrow Functions
(1) Type annotations for arrow functions
const add = (a: number, b: number): number => a + b;
// When acting as a callback,Parameter types can usually be inferred
const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2); // n Inferred as number
const asStrings = nums.map(n => String(n)); // n Inferred as number
(2) Arrow Functions and this
Arrow functions do not bind their own this—they inherit the outer this:
class Timer {
seconds = 0;
start() {
// Arrow Functions——this Orientation Timer Examples
setInterval(() => {
this.seconds++;
console.log(`${this.seconds}s`);
}, 1000);
}
startBroken() {
// Ordinary Functions——this Points to the global or undefined(Strict Mode)
setInterval(function () {
// this.seconds++; // ❌ Runtime this No Timer
}, 1000);
}
}
▶ Example: Rest Parameters and Function Overloads
// Rest parameters — collecting variable arguments
function log(context: string, ...messages: string[]): void {
console.log(`[${context}] ${messages.join(", ")}`);
}
log("App", "started", "initialized", "ready");
// [App] started, initialized, ready
// Function overloads — different return types per signature
function parse(input: string): Date;
function parse(input: number): Date;
function parse(input: string | number): Date {
return typeof input === "string"
? new Date(input)
: new Date(input);
}
let fromStr = parse("2024-01-01");
let fromNum = parse(1704067200000);
console.log(fromStr.toISOString()); // "2024-01-01T00:00:00.000Z"
Output:
[App] started, initialized, ready
2024-01-01T00:00:00.000Z
❓ FAQ
return), but it’s recommended to specify it for functions longer than three lines.void and undefined?void means “the function does not care about the return value”—the function may or may not have a return statement, and the caller should not use the return value. undefined is a specific type—the function explicitly returns undefined. In actual development, functions with no return value all use void.function fn(x: string | number) can only describe that "a parameter can be one of these types," but all parameter combinations return the same type. Overloading allows for “different parameter combinations to return different types”—something union types cannot do. If the parameter types are flexible but the return type is uniform, using a union type is more concise; use overloading when different return types are required.function fn(x?: string = "hi") is a syntax error—default parameters are already implied to be "optional," so there is no need to add ?. x: string = "hi" is equivalent to x?: string, but its value is "hi" rather than undefined.📖 Summary
- Function type annotations: Parameter types must be annotated; return types are recommended to be annotated; "void" indicates no return value, and "never" indicates that the function never returns.
- Use
?for optional parameters (the value may be undefined); use= defaultValuefor default parameters (it’s safer to have a default value) - Remaining parameters
...args: type[]Collects excess parameters into an array - Function-type expressions
type Fn = (parameter) => returnValueare used to describe callbacks and higher-order functions - Function overloading follows a "declaration + implementation" pattern—the declaration specifies the return type for each combination of parameters, and the implementation must be compatible with all overloads.
📝 Exercises
- Basic Exercise (Difficulty ⭐): Write three functions—
add(a, b)for addition,subtract(a, b)for subtraction, andmultiply(a, b)for multiplication—where both the parameters and return values are of typenumber. Use function-type expressions to uniformly declare the types. - Advanced Problem (Difficulty ⭐⭐): Write a function
buildQuery(params: Record<string, string | number | boolean>)that converts an object into a URL query string. For example,{ page: 1, size: 10, active: true }→"page=1&size=10&active=true". - Challenge (Difficulty ⭐⭐⭐): Implement this using function overloading
createElement(tag, props): When the tag is "input," props must betype: string; when the tag is "a," props must behref: string; and when the tag is "div," props are a generic object. Return a different type description object for each case.