⚡ Function Type Annotations
TypeScript allows you to add type annotations to function parameters and return values, making function signatures a clear contract.
Regular function syntax:
function add(a: number, b: number): number {
return a + b;
}
function greet(name: string): string {
return `你好,${name}!`;
}
// 返回 void 表示没有返回值
function log(message: string): void {
console.log(message);
}
Equivalent arrow function syntax:
const add = (a: number, b: number): number => a + b;
const greet = (name: string): string => `你好,${name}!`;
// 多行箭头函数
const processData = (input: string): string[] => {
const parts = input.split(",");
return parts.map(p => p.trim());
};
💡 Type Inference:
If the function body is simple, TypeScript can automatically infer the return type. But for public APIs and library functions, explicitly annotating the return type is best practice — it serves as documentation and prevents accidental return type changes.
❓ Optional Parameters and Default Values
Use ? to mark optional parameters, = to set default values, and ...args to collect rest parameters.
// 可选参数:middleName 可以不传
function fullName(first: string, last: string, middleName?: string): string {
return middleName ? `${first} ${middleName} ${last}` : `${first} ${last}`;
}
fullName("张", "三"); // "张 三"
fullName("张", "三", "小明"); // "张 小明 三"
// 默认值:有默认值的参数自动变为可选
function createUrl(path: string, base: string = "https://api.example.com"): string {
return `${base}${path}`;
}
createUrl("/users"); // "https://api.example.com/users"
createUrl("/users", "http://localhost"); // "http://localhost/users"
// 剩余参数:收集任意数量的参数
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3, 4, 5); // 15
⚠️ Note:
Optional parameters must come after required parameters. If you need a middle parameter to be optional, consider using object parameters or function overloading.
🔀 Function Overloading
When a function returns different types based on different input types, you can use overload signatures to precisely describe this behavior.
// 重载签名(仅声明,不实现)
function parse(input: string): number;
function parse(input: number): string;
// 实现签名(必须兼容所有重载)
function parse(input: string | number): number | string {
if (typeof input === "string") {
return parseInt(input, 10);
}
return input.toString();
}
const num = parse("42"); // 类型推断为 number
const str = parse(42); // 类型推断为 string
A more practical example — overloading by parameter count:
function createElement(tag: string): HTMLElement;
function createElement(tag: string, content: string): HTMLElement;
function createElement(tag: string, content?: string): HTMLElement {
const el = document.createElement(tag);
if (content) el.textContent = content;
return el;
}
💡 Overloading vs Union Types:
If there's no correspondence between input/output types, using union types is simpler. Only use overloading when the mapping "input A → returns X, input B → returns Y" needs to be precisely expressed.
📐 interface
Interfaces are used to describe the shape of objects and are one of TypeScript's most core type-building tools.
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
}
const user: User = {
id: 1,
name: "张三",
email: "zhang@example.com",
createdAt: new Date(),
};
// user.createdAt = new Date(); // ❌ 编译错误:readonly 不可修改
Index signatures — when you don't know the property names but know the value type:
interface StringMap {
[key: string]: string;
}
const headers: StringMap = {
"Content-Type": "application/json",
"Authorization": "Bearer token123",
};
interface NumberArray {
[index: number]: string;
length: number;
}
🔄 Difference from Java:
Java uses interface to define behavioral contracts, and classes must explicitly declare implements. TypeScript's interface is purely structural checking (duck typing) — objects are automatically compatible as long as their structure matches, without any explicit declaration. This means you can define interfaces for third-party library objects without modifying their source code.
🧬 Interface Inheritance and Composition
Interfaces can inherit from other interfaces using extends, supporting multiple inheritance.
interface HasId {
id: number;
}
interface HasTimestamps {
createdAt: Date;
updatedAt: Date;
}
// 继承多个接口
interface User extends HasId, HasTimestamps {
name: string;
email: string;
}
// 等价于手写所有属性:
// interface User {
// id: number;
// createdAt: Date;
// updatedAt: Date;
// name: string;
// email: string;
// }
const user: User = {
id: 1,
name: "李四",
email: "li@example.com",
createdAt: new Date(),
updatedAt: new Date(),
};
💡 Declaration Merging:
Interfaces with the same name automatically merge their properties (declaration merging). This is very useful for extending type definitions of third-party libraries, but can also cause surprises — watch out for naming conflicts.
🏷️ type Aliases
The type keyword can create aliases for any type, more flexible than interface.
// 基本别名
type ID = string | number;
type Status = "active" | "inactive" | "banned";
// 对象类型(与 interface 类似)
type Point = {
x: number;
y: number;
};
// 联合类型
type Result<T> = { success: true; data: T } | { success: false; error: string };
// 交叉类型(合并多个类型)
type WithId = { id: number };
type WithName = { name: string };
type Entity = WithId & WithName; // { id: number; name: string }
// 使用内置工具类型
type PartialUser = Partial<User>; // 所有属性变为可选
type ReadonlyUser = Readonly<User>; // 所有属性变为只读
type UserName = Pick<User, "name" | "email">; // 只取部分属性
🤔 type vs interface: How to choose?
- interface: Preferred for describing object shapes; supports declaration merging and extends inheritance; better for OOP style
- type: Union types, intersection types, tuples, and basic type aliases can only use type; more common in functional programming style
- Mixing both is common in real projects — the key is team consistency
🔗 Function Types and Callbacks
TypeScript can precisely describe the type signatures of callbacks and higher-order functions.
// 用 type 定义函数类型
type Callback = (data: string) => void;
type Predicate<T> = (item: T) => boolean;
type Transformer<T, U> = (input: T) => U;
// 使用函数类型
function fetchData(url: string, onSuccess: Callback, onError: Callback): void {
// ...
}
// 高阶函数:接收函数,返回函数
function filter<T>(arr: T[], predicate: Predicate<T>): T[] {
return arr.filter(predicate);
}
const numbers = [1, 2, 3, 4, 5];
const evens = filter(numbers, n => n % 2 === 0); // [2, 4]
// 工厂函数模式
type Logger = (message: string) => void;
function createLogger(prefix: string): Logger {
return (message: string) => {
console.log(`[${prefix}] ${message}`);
};
}
const appLog = createLogger("APP");
appLog("启动完成"); // "[APP] 启动完成"
💡 Interfaces can also describe functions:
interface SearchFunc {
(source: string, keyword: string): boolean;
}
const search: SearchFunc = (src, kw) => src.includes(kw);
But in practice, using type to define function types is more concise and more common.
📝 Chapter Summary
Function Types
Parameters and return values can be annotated; supports optional ?, defaults =, and rest parameters ...
Function Overloading
Multiple overload signatures + one implementation; used for precise input-output mapping
interface
Describes object shapes; supports optional/readonly properties, index signatures, inheritance, and declaration merging
type Aliases
More flexible than interface: union types, intersection types, and utility types all require type
Duck Typing
TypeScript's interfaces use structural type checking — no explicit implements declaration needed
Callbacks and Higher-Order Functions
Use type to define function signature types, giving callback parameters full type checking