Skip to content

第3章 控制流与函数

本章定位:深入学习TypeScript中函数的声明与使用,包括普通函数、箭头函数、泛型函数,以及参数类型注解、返回值类型、默认参数、可选参数等。


TypeScript的函数声明与Python的def类似,但语法不同:

// TypeScript函数声明
function greet(name: string): string {
return `Hello, ${name}!`;
}
// 调用
const message = greet("Alice");
console.log(message); // "Hello, Alice!"

Python对比:

def greet(name: str) -> str:
return f"Hello, {name}!"
# 调用
message = greet("Alice")
print(message) # "Hello, Alice!"
// 函数声明可以带有类型注解
function add(a: number, b: number): number {
return a + b;
}
// 函数本身也有类型
const myAdd: (x: number, y: number) => number = add;

箭头函数是ES6引入的语法,类似Python的lambda,但更强大:

// 普通函数
function square(n: number): number {
return n * n;
}
// 箭头函数
const squareArrow = (n: number): number => n * n;
// 单参数可以省略括号
const double = n => n * 2; // 不推荐,清晰度差
// 无参数箭头函数
const getRandom = () => Math.random();
// 多行箭头函数需要花括号
const calculate = (x: number, y: number): number => {
const temp = x * 2;
return temp + y;
};

Python对比:

# 普通函数
def square(n):
return n * n
# lambda(箭头函数的Python等价)
square_lambda = lambda n: n * n
# 多行需要用普通函数
def calculate(x, y):
temp = x * 2
return temp + y

普通函数中的this:

const person = {
name: "Alice",
greet: function() {
// this指向person对象
console.log(`Hello, ${this.name}`);
},
greetDelayed: function() {
setTimeout(function() {
// this在普通函数中指向window/undefined(在严格模式下)
console.log(`Hello, ${this.name}`); // undefined!
}, 1000);
}
};

箭头函数中的this:

const person = {
name: "Alice",
greet: function() {
// 箭头函数捕获外层的this
console.log(`Hello, ${this.name}`);
},
greetDelayed: function() {
setTimeout(() => {
// 箭头函数不重新绑定this
console.log(`Hello, ${this.name}`); // "Hello, Alice"
}, 1000);
}
};

Python无this概念,对应的是self:

class Person:
def __init__(self, name):
self.name = name
def greet(self):
print(f"Hello, {self.name}")
def greet_delayed(self):
import time
# Python threading 不改变 self 引用
print(f"Hello, {self.name}")
场景推荐原因
对象方法普通函数需要正确的this绑定
回调函数箭头函数保持外部this
高阶函数参数箭头函数简洁
需要递归普通函数箭头函数不能被引用
需要动态this普通函数箭头函数绑定外层this

// 单个参数
function greet(name: string): string {
return `Hello, ${name}!`;
}
// 多个参数
function add(a: number, b: number): number {
return a + b;
}
// 参数类型错误
add("1", "2"); // 编译错误!参数类型不匹配

TypeScript可以推断返回值类型,但显式标注更清晰:

// TypeScript推断返回number
function add(a: number, b: number) {
return a + b;
}
// 显式标注返回string
function formatName(first: string, last: string): string {
return `${last}, ${first}`;
}
// 返回void(不返回任何值)
function logMessage(message: string): void {
console.log(message);
}
// 返回never(永不返回)
function throwError(message: string): never {
throw new Error(message);
}
// 永不返回的另一种方式
function infiniteLoop(): never {
while (true) {
// 无限循环
}
}
// 推断(简洁,适合简单函数)
const add = (a: number, b: number) => a + b;
// 显式标注(清晰,适合复杂函数)
const calculate = (data: number[]): number => {
const sum = data.reduce((acc, n) => acc + n, 0);
return sum / data.length;
};
// void返回的推断
const log = (msg: string) => console.log(msg);
// 普通函数
function createUser(name: string, age: number) {
return { name, age };
}
// 构造函数
function User(name: string, age: number) {
this.name = name;
this.age = age;
}
User.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
// 使用new调用
const user = new User("Alice", 30);
console.log(user.greet()); // "Hello, I'm Alice"

Python对比:

class User:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def greet(self) -> str:
return f"Hello, I'm {self.name}"

使用?标记可选参数:

// name是必需的,greeting是可选的
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`;
}
return `Hello, ${name}!`;
}
greet("Alice"); // "Hello, Alice!"
greet("Alice", "Hi"); // "Hi, Alice!"
// 可选参数必须在必需参数之后
function greet(greeting: string, name?: string): string {
// 这是合法的
}
// 错误示例:可选参数不能在必需参数之前
// function greet(name?: string, greeting: string) // Error!

Python对比:

def greet(name: str, greeting: str | None = None) -> str:
if greeting:
return f"{greeting}, {name}!"
return f"Hello, {name}!"
greet("Alice") # "Hello, Alice!"
greet("Alice", "Hi") # "Hi, Alice!"

使用=设置默认值:

function createUser(
name: string,
age: number = 18,
role: string = "user"
): User {
return { name, age, role };
}
createUser("Alice"); // {name: "Alice", age: 18, role: "user"}
createUser("Bob", 25); // {name: "Bob", age: 25, role: "user"}
createUser("Charlie", 30, "admin"); // {name: "Charlie", age: 30, role: "admin"}

Python对比:

def create_user(name: str, age: int = 18, role: str = "user") -> dict:
return {"name": name, "age": age, "role": role}
create_user("Alice") # {"name": "Alice", "age": 18, "role": "user"}
create_user("Bob", 25) # {"name": "Bob", "age": 25, "role": "user"}
// 默认参数让TypeScript推断可选
function logMessage(message: string, level: string = "info"): void {
console.log(`[${level}] ${message}`);
}
// level可以是string | undefined
logMessage("Hello"); // OK
logMessage("Hello", "error"); // OK
logMessage("Hello", undefined); // OK,但没必要
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
// 显式传递undefined会使用默认值
greet("Alice", undefined); // "Hello, Alice!"
// 显式传递undefined vs 不传
greet("Alice"); // "Hello, Alice!"
greet("Alice", undefined); // "Hello, Alice!"(效果相同)

使用...语法收集剩余参数为数组:

// 收集所有额外参数为数组
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
sum(); // 0
// 普通参数 + 剩余参数
function logMessage(level: string, ...messages: string[]): void {
messages.forEach(msg => console.log(`[${level}] ${msg}`));
}
logMessage("info", "Hello", "World", "!");
// [info] Hello
// [info] World
// [info] !

Python对比:

# Python使用*args收集剩余参数
def sum(*numbers):
return sum(numbers)
sum(1, 2, 3) # 6
sum(1, 2, 3, 4, 5) # 15
def log_message(level, *messages):
for msg in messages:
print(f"[{level}] {msg}")
log_message("info", "Hello", "World", "!")
// number数组
function collect(...args: number[]): number[] {
return args;
}
// string数组
function join(...parts: string[]): string {
return parts.join("-");
}
// 混合类型(联合类型)
function mixed(...args: (string | number)[]): (string | number)[] {
return args;
}
// 对象数组
function createUsers(...users: { name: string; age: number }[]): User[] {
return users;
}
  1. 只能有一个剩余参数

    // 错误:多个剩余参数
    // function f(...a: number[], ...b: string[]) {}
  2. 剩余参数必须在最后

    // 错误:剩余参数不在最后
    // function f(...args: number[], extra: string) {}
  3. 剩余参数不能有默认值

    // 错误:剩余参数不能有默认值
    // function f(...args: number[] = []) {}
// 接收对象解构
function createUser({ name, age, role = "user" }: {
name: string;
age: number;
role?: string;
}): User {
return { name, age, role };
}
// 使用展开运算符传入对象
const userData = { name: "Alice", age: 30 };
createUser(userData); // OK

Python对比:

def create_user(name: str, age: int, role: str = "user") -> dict:
return {"name": name, "age": age, "role": role}
# Python解包
user_data = {"name": "Alice", "age": 30}
create_user(**user_data) # OK

TypeScript支持函数重载,允许同一函数名有多个函数签名:

// 函数重载签名
function reverse(str: string): string;
function reverse(arr: string[]): string[];
function reverse(strOrArr: string | string[]): string | string[] {
if (typeof strOrArr === "string") {
return strOrArr.split("").reverse().join("");
} else {
return strOrArr.reverse();
}
}
reverse("hello"); // "olleh"
reverse(["a", "b", "c"]); // ["c", "b", "a"]
reverse(123); // 编译错误!
  1. 两个或更多的重载签名
  2. 最后一个签名是实现签名
  3. 实现签名参数类型必须是所有重载签名的超类型
// 错误示例:实现签名不是超类型
// function f(x: string): void;
// function f(x: number): void;
// function f(x: string | number): void; // OK

处理不同输入类型:

// 多种参数组合
interface Point {
x: number;
y: number;
}
function createPoint(): Point;
function createPoint(x: number): Point;
function createPoint(x: number, y: number): Point;
function createPoint(x?: number, y?: number): Point {
return {
x: x ?? 0,
y: y ?? 0
};
}
createPoint(); // {x: 0, y: 0}
createPoint(10); // {x: 10, y: 0}
createPoint(10, 20); // {x: 10, y: 20}

工厂模式:

class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name);
}
}
class Cat extends Animal {
constructor(name: string, public color: string) {
super(name);
}
}
function createAnimal(type: "dog", name: string, breed: string): Dog;
function createAnimal(type: "cat", name: string, color: string): Cat;
function createAnimal(type: "dog" | "cat", name: string, extra: string): Animal {
if (type === "dog") {
return new Dog(name, extra);
} else {
return new Cat(name, extra);
}
}
const dog = createAnimal("dog", "Buddy", "Golden");
const cat = createAnimal("cat", "Whiskers", "Orange");
// 使用联合类型
function process(input: string | string[]): string | string[] {
if (typeof input === "string") {
return input.toUpperCase();
}
return input.map(s => s.toUpperCase());
}
// 使用重载
function processString(input: string): string;
function processString(input: string[]): string[];
function processString(input: string | string[]): string | string[] {
if (typeof input === "string") {
return input.toUpperCase();
}
return input.map(s => s.toUpperCase());
}
// 重载更明确,IDE提示更好
// 联合类型更简洁,适合简单场景

泛型允许函数操作多种类型而不丢失类型信息:

// 泛型函数:T是类型参数
function identity<T>(arg: T): T {
return arg;
}
// 使用时指定类型
const num = identity<number>(42); // number
const str = identity<string>("hello"); // string
// 让TypeScript推断类型
const num2 = identity(42); // number
const str2 = identity("hello"); // string

Python对比:

from typing import TypeVar
T = TypeVar("T")
def identity(x: T) -> T:
return x
num = identity(42) # int
str = identity("hello") # str
// 多个泛型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
const p1 = pair<string, number>("age", 30); // [string, number]
const p2 = pair("name", "Alice"); // [string, string],推断

使用extends限制类型参数的范围:

// 要求T必须有length属性
function logLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
logLength("hello"); // 5
logLength([1, 2, 3]); // 3
logLength({ length: 10 }); // 10
logLength(123); // 编译错误!number没有length
// 约束T必须是某种类型
function getProperty<T extends object, K extends keyof T>(
obj: T,
key: K
): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 30 };
const name = getProperty(user, "name"); // string
const age = getProperty(user, "age"); // number
getProperty(user, "email"); // 编译错误!"email"不在user中

Python对比:

from typing import TypeVar
T = TypeVar("T")
def get_property(obj: dict, key: str):
return obj[key]
# Python无法在编译时检查key是否有效
user = {"name": "Alice", "age": 30}
name = get_property(user, "name") # "Alice"
age = get_property(user, "age") # 30
email = get_property(user, "email") # 运行时才报错
// 泛型数组操作
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const nums = firstElement([1, 2, 3]); // number | undefined
const strs = firstElement(["a", "b"]); // string | undefined
// 约束为number数组
function sum<T extends number>(arr: T[]): number {
return arr.reduce((acc, n) => acc + n, 0);
}
sum([1, 2, 3]); // 6
sum(["a", "b"]); // 编译错误!string不是number的子类型
// 泛型 + 回调函数
function map<T, U>(arr: T[], fn: (item: T) => U): U[] {
return arr.map(fn);
}
const numbers = [1, 2, 3];
const squared = map(numbers, n => n * n); // number[]
const strings = map(numbers, n => `Number: ${n}`); // string[]
// Promise泛型
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json() as T;
}
const user = await fetchData<User>("https://api.example.com/user");

TypeScript 2.3+ 支持泛型默认类型:

// 默认类型参数
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
const strArr = createArray(3); // string[](使用默认)
const numArr = createArray(3, 42); // number[]
const boolArr = createArray<boolean>(3, true); // boolean[]

PythonTypeScript说明
def f(x):function f(x: type):函数声明
-> str: string返回类型标注位置
lambda x: x(x: type) => x箭头函数
*args...args: type[]剩余参数
**kwargs{...}: { key: type }命名参数
def f(x=5)function f(x: number = 5)默认参数
# Python:基础函数
def add(a: int, b: int) -> int:
return a + b
# TypeScript
const add = (a: number, b: number): number => a + b;
// 或
function add(a: number, b: number): number {
return a + b;
}
# Python:可选参数
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}!"
# TypeScript
const greet = (name: string, greeting: string = "Hello"): string =>
`${greeting}, ${name}!`;
# Python:可变参数
def sum(*args: int) -> int:
total = 0
for n in args:
total += n
return total
# TypeScript
const sum = (...args: number[]): number =>
args.reduce((acc, n) => acc + n, 0);
# Python:lambda
square = lambda x: x ** 2
# 或
def square(x): return x ** 2
# TypeScript
const square = (x: number): number => x ** 2;
# Python:类型约束(有限)
from typing import TypeVar
T = TypeVar("T", int, float)
def double(x: T) -> T:
return x * 2
# TypeScript
function double<T extends number | string>(x: T): T {
return x as any * 2; // 需要类型断言
}
# Python:位置参数和关键字参数
def create_user(name, age, role="user"):
return {"name": name, "age": age, "role": role}
create_user("Alice", 30) # 位置
create_user("Bob", 25, role="admin") # 混合
create_user(age=35, name="Charlie") # 关键字(顺序无关)
// TypeScript:位置参数
function createUser(name: string, age: number, role: string = "user"): User {
return { name, age, role };
}
createUser("Alice", 30); // 位置
createUser("Bob", 25, "admin"); // 位置
// createUser({ name: "Charlie", age: 35 }); // 不支持关键字参数
// 但可以用对象解构
function createUser({ name, age, role = "user" }: {
name: string;
age: number;
role?: string;
}): User {
return { name, age, role };
}
createUser({ name: "Alice", age: 30 }); // 类似关键字参数
# Python:可以返回多个值
def get_stats(numbers):
return len(numbers), sum(numbers), min(numbers), max(numbers)
count, total, min_val, max_val = get_stats([1, 2, 3, 4, 5])
// TypeScript:返回元组实现多值
function getStats(numbers: number[]): [number, number, number, number] {
return [
numbers.length,
numbers.reduce((a, b) => a + b, 0),
Math.min(...numbers),
Math.max(...numbers)
];
}
const [count, total, minVal, maxVal] = getStats([1, 2, 3, 4, 5]);
# Python:type hint仅供阅读,不强制
def add(a: int, b: int) -> int:
return a + b
add("1", "2") # 运行时报错:TypeError
// TypeScript:编译时检查
const add = (a: number, b: number): number => a + b;
add("1", "2"); // 编译错误!类型不匹配
# Python:函数作为参数和返回值
def apply_twice(func, x):
return func(func(x))
def add_five(x):
return x + 5
result = apply_twice(add_five, 10) # 20
# 返回函数
def create_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
// TypeScript:函数作为参数和返回值
function applyTwice<T>(func: (x: T) => T, x: T): T {
return func(func(x));
}
const addFive = (x: number): number => x + 5;
const result = applyTwice(addFive, 10); // 20
// 返回函数
function createMultiplier(factor: number): (x: number) => number {
return (x: number) => x * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15

本章学习了TypeScript的函数与控制流:

  1. 函数声明与箭头函数:

    • 普通函数使用function关键字,箭头函数使用=>
    • 箭头函数不绑定自己的this
    • 箭头函数不能用作构造函数
  2. 参数与返回值类型:

    • 参数类型注解在参数名后
    • 返回值类型注解在参数列表后
    • 可以省略,让TypeScript推断
  3. 可选参数与默认参数:

    • ?标记可选参数
    • =设置默认值
    • 可选参数在必需参数之后
  4. 剩余参数:

    • ...args: type[]收集额外参数
    • 只能有一个剩余参数
    • 必须在参数列表最后
  5. 函数重载:

    • 多个函数签名,一个实现
    • 最后一个签名是实现签名
    • 适合处理多种输入类型
  6. 泛型函数:

    • <T>定义类型参数
    • extends约束类型参数
    • 可以有多个类型参数
  7. Python对比:

    • Python用def,TypeScript用function或箭头函数
    • Python参数顺序更灵活(关键字参数)
    • TypeScript编译时检查更严格

编写以下函数:

  • reverseString(str: string): string - 反转字符串
  • isEven(n: number): boolean - 判断偶数

编写formatDate(date: Date, format?: string): string函数:

  • 无format参数时返回”YYYY-MM-DD”
  • 有format参数时按format格式化

编写merge(...arrays: string[][]): string[]函数,合并多个数组。

编写parse(input: string): Date或parse(input: number): Date,根据输入类型返回不同结果。

编写unique<T>(arr: T[]): T[]函数,返回去重后的数组。

用Python和TypeScript分别实现以下功能,比较语法差异:

  • 一个计算器函数,接受操作符和两个数字
  • 一个高阶函数,接收函数并应用两次


下一章我们将学习TypeScript的复合类型,包括数组、元组、对象、接口、联合类型等,掌握如何构建复杂的数据结构。