惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
H
Help Net Security
V
Visual Studio Blog
J
Java Code Geeks
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
The Cloudflare Blog
Martin Fowler
Martin Fowler
D
Docker
腾讯CDC
F
Fortinet All Blogs
雷峰网
雷峰网
GbyAI
GbyAI
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Blog — PlanetScale
Blog — PlanetScale
Engineering at Meta
Engineering at Meta
博客园 - 聂微东
博客园 - 叶小钗

博客园 - thammer

03 — std::vector 进阶篇 02 — 迭代器基础 01 — std::vector 基础篇 Day 17:Promise 基础 Day 16 - 读懂 TypeScript 声明文件(.d.ts) Day 15 - ArkTS 模块系统 Day 14 - ArkTS 错误处理 Day 13 - ArkTS 类型系统进阶 Day 12 - 泛型与阶段三总结 Day 11 - Interface 与类型契约 Day 10 - 继承与多态 Day 09 - Class 基础 Day 07 - 函数类型与高阶函数 Day 06 - 函数基础 Day 05 - 数组和元组深入 Day 04 - 循环与迭代 Day 03 - 条件控制 Day 02 - 运算符与表达式 Day 01 - 变量与类型基础 Day 00 - 环境准备 C++虚函数 C++ 多继承 C++成员访问权限以及继承权限 C++ 成员初始化机制总结笔记
Day 08 - 函数进阶与阶段总结
thammer · 2026-04-13 · via 博客园 - thammer

目标:掌握函数重载、递归函数、typeof 类型守卫,完成 Phase 02 阶段总结
预计时间:1.5-2小时
前置知识:Day 06 函数基础、Day 07 函数类型与回调


第一部分:函数重载

1.1 为什么需要函数重载

在实际开发中,我们经常需要一个函数处理不同类型的输入。例如,一个格式化函数可能需要处理数字、字符串或布尔值。

// 不用重载:需要写多个函数,函数名难以管理
function formatNumber(value: number): string {
    return value.toFixed(2);
}

function formatString(value: string): string {
    return value.toUpperCase();
}

function formatBoolean(value: boolean): string {
    return value ? "是" : "否";
}

// 使用重载:一个函数名,多种调用方式
// 见下文...

C/C++对比:
C++ 支持真正的函数重载,编译器会根据参数类型自动选择对应的函数实现:

// C++ 函数重载
std::string format(double value) { return std::to_string(value); }
std::string format(const std::string& value) { return value; }
std::string format(bool value) { return value ? "true" : "false"; }

// 编译器自动选择
format(3.14);        // 调用 format(double)
format("hello");     // 调用 format(const std::string&)

1.2 ArkTS 重载语法

ArkTS 的函数重载与 C++ 有本质区别:不是写多个同名函数,而是写多个重载签名 + 一个统一实现

// 重载签名(只有声明,没有实现)
function format(value: number): string;
function format(value: string): string;
function format(value: boolean): string;

// 实现签名(必须兼容所有重载签名)
function format(value: number | string | boolean): string {
    if (typeof value === 'number') {
        return value.toFixed(2);
    } else if (typeof value === 'string') {
        return value.toUpperCase();
    } else {
        return value ? "是" : "否";
    }
}

// 使用
let r1: string = format(3.14159);     // "3.14"
let r2: string = format("hello");     // "HELLO"
let r3: string = format(true);        // "是"

关键要点:

  1. 重载签名可以有多个,每个定义一种调用方式
  2. 实现签名只有一个,参数类型必须是所有重载签名的联合类型
  3. 实现签名内部用 typeof 判断实际类型,执行不同逻辑

1.3 参数类型重载

根据参数类型的不同执行不同逻辑:

// 处理设备ID:可以是数字ID或设备名称
function findDevice(id: number): string;
function findDevice(name: string): string;

function findDevice(param: number | string): string {
    if (typeof param === 'number') {
        return `查找ID为 ${param} 的设备`;
    } else {
        return `查找名称为 "${param}" 的设备`;
    }
}

console.log(findDevice(1001));           // "查找ID为 1001 的设备"
console.log(findDevice("客厅灯"));        // "查找名称为 "客厅灯" 的设备"

另一个例子 - 计算面积:

// 计算不同形状的面积
function calculateArea(radius: number): number;                    // 圆
function calculateArea(width: number, height: number): number;     // 矩形

function calculateArea(a: number, b?: number): number {
    if (b === undefined) {
        // 圆形:π * r²
        return 3.14159 * a * a;
    } else {
        // 矩形:宽 * 高
        return a * b;
    }
}

console.log(`圆面积: ${calculateArea(5)}`);           // 78.54
console.log(`矩形面积: ${calculateArea(4, 6)}`);      // 24

1.4 参数数量重载

根据参数个数的不同执行不同逻辑:

// 创建配置:不同数量的参数
function createConfig(name: string): string;
function createConfig(name: string, value: number): string;
function createConfig(name: string, value: number, unit: string): string;

function createConfig(name: string, value?: number, unit?: string): string {
    if (value === undefined) {
        return `配置项: ${name}`;
    } else if (unit === undefined) {
        return `配置项: ${name} = ${value}`;
    } else {
        return `配置项: ${name} = ${value} ${unit}`;
    }
}

console.log(createConfig("温度阈值"));                    // "配置项: 温度阈值"
console.log(createConfig("亮度", 80));                    // "配置项: 亮度 = 80"
console.log(createConfig("电压", 220, "V"));              // "配置项: 电压 = 220 V"

1.5 重载的注意事项

1. 实现签名必须兼容所有重载签名

// ❌ 错误:实现签名参数类型不包含所有重载签名的情况
function process(x: number): void;
function process(x: string): void;
function process(x: number): void {  // 错误!缺少 string 的处理
    // ...
}

// ✅ 正确:实现签名使用联合类型
function process(x: number): void;
function process(x: string): void;
function process(x: number | string): void {
    // ...
}

2. 重载签名顺序很重要

编译器会按顺序匹配重载签名,更具体的签名应该放在前面:

// ❌ 错误:通用签名在前,具体签名无法匹配
function handle(x: number | string): void;
function handle(x: number): void;  // 这行永远不会被匹配到

// ✅ 正确:具体签名在前
function handle(x: number): void;
function handle(x: string): void;
function handle(x: number | string): void {
    // ...
}

3. 返回值类型也要匹配

// 重载签名声明的返回值类型,实现签名必须满足
function getValue(id: number): number;
function getValue(name: string): string;

function getValue(param: number | string): number | string {
    if (typeof param === 'number') {
        return param * 10;  // 返回 number
    } else {
        return `Value of ${param}`;  // 返回 string
    }
}

C/C++对比:

  • C++ 重载是编译时决议,每个重载有独立的函数体
  • ArkTS 重载是运行时判断,只有一个函数体,内部用条件分支处理
  • ArkTS 重载更像 C 语言中用 void* 或联合体模拟的重载,但类型安全

第二部分:递归函数

2.1 递归的基本概念

递归是函数调用自身的编程技巧。作为有 14 年 C/C++ 经验的开发者,你对递归一定很熟悉。

递归函数必须包含两个要素:

  1. 终止条件(Base Case):防止无限递归
  2. 递归步骤(Recursive Step):向终止条件靠近
// 经典例子:计算阶乘
// n! = n * (n-1) * (n-2) * ... * 1
// 递归定义:n! = n * (n-1)!, 且 0! = 1, 1! = 1

function factorial(n: number): number {
    // 终止条件
    if (n <= 1) {
        return 1;
    }
    // 递归步骤
    return n * factorial(n - 1);
}

console.log(`5! = ${factorial(5)}`);   // 120
console.log(`0! = ${factorial(0)}`);   // 1

C/C++对比:

// C++ 实现完全相同
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

2.2 经典案例

案例1:斐波那契数列

// 斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13, 21...
// F(0) = 0, F(1) = 1
// F(n) = F(n-1) + F(n-2)

function fibonacci(n: number): number {
    if (n === 0) {
        return 0;
    }
    if (n === 1) {
        return 1;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

console.log(`F(10) = ${fibonacci(10)}`);   // 55
console.log(`F(20) = ${fibonacci(20)}`);   // 6765

案例2:计算幂次

// 计算 base 的 exponent 次幂
function power(base: number, exponent: number): number {
    // 终止条件:任何数的0次方都是1
    if (exponent === 0) {
        return 1;
    }
    // 负指数处理
    if (exponent < 0) {
        return 1 / power(base, -exponent);
    }
    // 递归步骤
    return base * power(base, exponent - 1);
}

console.log(`2^10 = ${power(2, 10)}`);     // 1024
console.log(`5^0 = ${power(5, 0)}`);       // 1
console.log(`2^-3 = ${power(2, -3)}`);     // 0.125

2.3 递归处理数组

案例1:递归求和

// 计算数组所有元素的和
function sumArray(arr: number[]): number {
    // 终止条件:空数组
    if (arr.length === 0) {
        return 0;
    }
    // 取第一个元素 + 剩余元素的和
    return arr[0] + sumArray(arr.slice(1));
}

let numbers: number[] = [1, 2, 3, 4, 5];
console.log(`数组求和: ${sumArray(numbers)}`);   // 15

案例2:递归查找最大值

// 递归查找数组中的最大值
function findMax(arr: number[]): number {
    // 终止条件:只有一个元素
    if (arr.length === 1) {
        return arr[0];
    }
    // 比较第一个元素和剩余元素的最大值
    let subMax: number = findMax(arr.slice(1));
    return arr[0] > subMax ? arr[0] : subMax;
}

let values: number[] = [23, 56, 12, 89, 34];
console.log(`最大值: ${findMax(values)}`);   // 89

案例3:递归查找元素

// 递归查找元素在数组中的索引,找不到返回 -1
function findIndex(arr: number[], target: number): number {
    return findIndexHelper(arr, target, 0);
}

function findIndexHelper(arr: number[], target: number, index: number): number {
    // 终止条件1:遍历完数组
    if (index >= arr.length) {
        return -1;
    }
    // 终止条件2:找到目标
    if (arr[index] === target) {
        return index;
    }
    // 递归步骤:检查下一个位置
    return findIndexHelper(arr, target, index + 1);
}

let data: number[] = [10, 20, 30, 40, 50];
console.log(`30 的索引: ${findIndex(data, 30)}`);   // 2
console.log(`99 的索引: ${findIndex(data, 99)}`);   // -1

2.4 递归注意事项

1. 终止条件的重要性

没有终止条件会导致无限递归,最终栈溢出:

// ❌ 错误:缺少终止条件
function badRecursive(n: number): number {
    return n + badRecursive(n - 1);  // 永远不会停止!
}

// ✅ 正确:有终止条件
function goodRecursive(n: number): number {
    if (n <= 0) {
        return 0;  // 终止条件
    }
    return n + goodRecursive(n - 1);
}

2. 栈溢出风险

每次递归调用都会消耗栈空间,递归深度过大时会导致栈溢出:

// 危险:计算大数的斐波那契会导致栈溢出
// fibonacci(10000);  // 可能栈溢出

// 优化方案1:使用尾递归(ArkTS引擎可能不优化,但代码更清晰)
function factorialTail(n: number, acc: number = 1): number {
    if (n <= 1) {
        return acc;
    }
    return factorialTail(n - 1, n * acc);
}

// 优化方案2:使用循环(推荐用于大数)
function factorialIterative(n: number): number {
    let result: number = 1;
    for (let i: number = 2; i <= n; i++) {
        result = result * i;
    }
    return result;
}

3. 递归深度建议

  • 一般递归深度建议控制在 1000 以内
  • 对于大数据量处理,优先考虑循环而非递归
  • 树形结构遍历适合用递归,因为深度通常可控

C/C++对比:

  • C/C++ 和 ArkTS 都有栈溢出风险
  • C++ 编译器可能对尾递归进行优化,ArkTS 引擎优化不确定
  • 嵌入式开发中,递归使用要格外谨慎(栈空间有限)

第三部分:typeof 类型守卫

3.1 问题引入

当函数参数是联合类型时,如何在函数体内安全地处理不同类型的值?

// 联合类型参数
function processValue(value: number | string): string {
    // ❌ 错误:无法确定 value 是 number 还是 string
    // return value.toFixed(2);  // 编译错误:string 没有 toFixed 方法
    
    // ❌ 错误:同样的问题
    // return value.toUpperCase();  // 编译错误:number 没有 toUpperCase 方法
    
    return "unknown";
}

我们需要一种机制在运行时判断类型,并帮助编译器收窄类型范围。


3.2 typeof 判断

ArkTS 提供 typeof 运算符来检测值的类型:

function processValue(value: number | string): string {
    if (typeof value === 'number') {
        // 在这个代码块中,编译器知道 value 是 number 类型
        return `数字: ${value.toFixed(2)}`;
    } else {
        // 在这个代码块中,编译器知道 value 是 string 类型
        return `字符串: ${value.toUpperCase()}`;
    }
}

console.log(processValue(3.14159));   // "数字: 3.14"
console.log(processValue("hello"));   // "字符串: HELLO"

typeof 可检测的类型:

  • "string" - 字符串
  • "number" - 数字
  • "boolean" - 布尔值
  • "undefined" - 未定义
  • "object" - 对象(注意:null 也是 object)
  • "function" - 函数

3.3 实际应用

应用1:格式化输出函数

function formatOutput(value: number | string | boolean): string {
    if (typeof value === 'number') {
        return `数值: ${value}`;
    } else if (typeof value === 'string') {
        return `文本: "${value}"`;
    } else {
        return `状态: ${value ? "开启" : "关闭"}`;
    }
}

console.log(formatOutput(42));        // "数值: 42"
console.log(formatOutput("设备A"));   // "文本: "设备A""
console.log(formatOutput(true));      // "状态: 开启"

应用2:安全计算函数

function safeAdd(a: number | string, b: number | string): string {
    if (typeof a === 'number' && typeof b === 'number') {
        // 两个都是数字,执行加法
        return `结果: ${a + b}`;
    } else {
        // 至少有一个是字符串,执行字符串拼接
        let strA: string = typeof a === 'number' ? a.toString() : a;
        let strB: string = typeof b === 'number' ? b.toString() : b;
        return `拼接: ${strA}${strB}`;
    }
}

console.log(safeAdd(10, 20));           // "结果: 30"
console.log(safeAdd("Hello", "World")); // "拼接: HelloWorld"
console.log(safeAdd(10, "个设备"));      // "拼接: 10个设备"

应用3:处理可选参数

function configureDevice(deviceId: number, name: string, extra?: number | string): void {
    console.log(`配置设备 ${deviceId}: ${name}`);
    
    if (extra !== undefined) {
        if (typeof extra === 'number') {
            console.log(`数值参数: ${extra}`);
        } else {
            console.log(`字符串参数: ${extra}`);
        }
    }
}

configureDevice(1001, "温度传感器");
configureDevice(1002, "湿度传感器", 5000);       // 数值参数
configureDevice(1003, "压力传感器", "kPa");      // 字符串参数

3.4 重要限制

typeof 用于运行时判断 vs 类型查询

ArkTS 中 typeof 有两种完全不同的用途,必须严格区分:

// ✅ 用途1:运行时类型判断(函数参数或局部变量均可)
function handleParam(value: number | string): void {
    if (typeof value === 'number') {
        console.log(`数字: ${value.toFixed(2)}`);
    } else {
        console.log(`字符串: ${value.toUpperCase()}`);
    }
}

// ✅ 局部变量同样可以使用 typeof 运行时判断
function handleLocal(): void {
    let value: number | string = 42;
    if (typeof value === 'number') {
        console.log(`数字: ${value}`);
    } else {
        console.log(`字符串: ${value}`);
    }
}

// ❌ 用途2:类型查询(ArkTS 禁止)
let x: number = 10;
type T = typeof x;  // 编译错误!ArkTS 禁止 typeof 用于类型查询

两种用途的本质区别:

用途 语法形式 ArkTS 是否支持
运行时类型判断 if (typeof x === 'number') ✅ 支持
编译期类型查询 type T = typeof x ❌ 禁止

C/C++对比:

  • C/C++ 没有 typeof 类型守卫的概念
  • C++ 用函数重载或模板处理多类型
  • C 语言通常用宏或 void* 配合类型标记处理

第四部分:阶段二总结

4.1 函数知识全景图

Day 06 - Day 08 三天所学内容的关系:

                    函数基础(Day 06)
                         │
        ┌────────────────┼────────────────┐
        │                │                │
   函数定义          参数类型          箭头函数
   function         必需/可选/默认       () => {}
        │                │                │
        └────────────────┼────────────────┘
                         │
              函数类型与回调(Day 07)
                         │
        ┌────────────────┼────────────────┐
        │                │                │
   函数类型          回调函数          高阶函数
   (x)=>T           作为参数          返回函数
        │                │                │
        └────────────────┼────────────────┘
                         │
              函数进阶(Day 08)
                         │
        ┌────────────────┼────────────────┐
        │                │                │
   函数重载          递归函数          类型守卫
   多签名+实现       自调用            typeof

4.2 关键概念对比表

参数类型对比:

类型 语法 调用时可省略 未提供时的值
必需参数 x: number -
可选参数 x?: number undefined
默认参数 x: number = 10 默认值
剩余参数 ...x: number[] []

函数形式对比:

特性 函数声明 箭头函数
语法 function foo() {} () => {}
适合场景 独立函数、递归 回调、简短函数
代码量 较多 简洁
可读性 清晰 需要适应

重载实现对比:

特性 C++ ArkTS
实现方式 多个函数体 一个函数体 + 条件分支
决议时机 编译时 运行时
性能 无运行时开销 有类型判断开销
灵活性 签名必须不同 可用联合类型统一处理

4.3 从 C/C++ 到 ArkTS 的函数差异总结

概念 C/C++ ArkTS 注意事项
函数声明 int add(int a, int b) function add(a: number, b: number): number 参数名在前,类型在后
无返回值 void func() function func(): void 语法类似
默认参数 int timeout = 5000 timeout: number = 5000 语法类似
函数指针 std::function / 函数指针 函数类型 / 箭头函数 ArkTS 更简洁
Lambda [](int x) { return x * 2; } (x: number) => x * 2 语法更短
函数重载 多个函数定义 多个签名 + 一个实现 实现方式完全不同
递归 支持 支持 都要注意栈溢出
类型判断 typeid / dynamic_cast typeof ArkTS 更简单

最重要的三点差异:

  1. 函数是第一类公民:ArkTS 中函数可以像变量一样赋值、传递、返回,比 C/C++ 的函数指针更自然

  2. 函数重载机制不同:C++ 是编译时多态,多个函数体;ArkTS 是运行时判断,一个函数体内部用 typeof 分支处理

  3. 类型守卫:ArkTS 的 typeof 可以在运行时收窄联合类型,C/C++ 需要依赖虚函数或 RTTI


第五部分:综合练习

练习 1:函数重载

编写一个 convertTemperature 函数,支持以下重载:

  • 接收摄氏度(number),返回华氏度(number)
  • 接收华氏度(number)和一个标识字符串 "F",返回摄氏度(number)

转换公式:

  • 摄氏度转华氏度:F = C × 9/5 + 32
  • 华氏度转摄氏度:C = (F - 32) × 5/9
参考答案
// 摄氏度转华氏度
function convertTemperature(celsius: number): number;
// 华氏度转摄氏度
function convertTemperature(fahrenheit: number, unit: string): number;

function convertTemperature(value: number, unit?: string): number {
    if (unit === undefined) {
        // 摄氏度转华氏度
        return value * 9 / 5 + 32;
    } else {
        // 华氏度转摄氏度
        return (value - 32) * 5 / 9;
    }
}

console.log(`25°C = ${convertTemperature(25)}°F`);        // 77
console.log(`77°F = ${convertTemperature(77, "F")}°C`);   // 25

练习 2:递归函数

2.1 实现 sumDigits 函数,递归计算一个正整数各位数字之和。

示例:sumDigits(12345) 返回 15(1+2+3+4+5)

2.2 实现 reverseString 函数,递归反转字符串。

示例:reverseString("hello") 返回 "olleh"

参考答案
// 2.1 各位数字之和
function sumDigits(n: number): number {
    if (n < 10) {
        return n;
    }
    return n % 10 + sumDigits(Math.floor(n / 10));
}

console.log(`sumDigits(12345) = ${sumDigits(12345)}`);  // 15

// 2.2 递归反转字符串
function reverseString(str: string): string {
    if (str.length <= 1) {
        return str;
    }
    return reverseString(str.substring(1)) + str.charAt(0);
}

console.log(`reverseString("hello") = ${reverseString("hello")}`);  // olleh

练习 3:typeof 类型守卫

编写 processSensorData 函数,参数为 data: number | number[] | string

  • 如果是 number,返回 "单个读数: X"
  • 如果是 number[],返回 "批量读数: X个数据"
  • 如果是 string,返回 "状态信息: X"
参考答案
function processSensorData(data: number | number[] | string): string {
    if (typeof data === 'number') {
        return `单个读数: ${data}`;
    } else if (typeof data === 'string') {
        return `状态信息: ${data}`;
    } else {
        // 到这里 data 一定是 number[]
        return `批量读数: ${data.length}个数据`;
    }
}

console.log(processSensorData(25.5));                    // "单个读数: 25.5"
console.log(processSensorData([20, 21, 22, 23]));        // "批量读数: 4个数据"
console.log(processSensorData("传感器正常"));             // "状态信息: 传感器正常"

练习 4:综合应用

实现一个 calculate 函数,支持以下重载:

  • calculate(a: number, b: number): number - 返回两数之和
  • calculate(a: number, b: number, operation: string): number - 根据 operation 执行不同运算

operation 可以是:

  • "add" - 加法
  • "subtract" - 减法
  • "multiply" - 乘法
  • "divide" - 除法(注意除零检查)
参考答案
// 两数相加
function calculate(a: number, b: number): number;
// 指定运算
function calculate(a: number, b: number, operation: string): number;

function calculate(a: number, b: number, operation?: string): number {
    let op: string = operation ?? "add";

    if (op === "add") {
        return a + b;
    } else if (op === "subtract") {
        return a - b;
    } else if (op === "multiply") {
        return a * b;
    } else if (op === "divide") {
        if (b === 0) {
            return 0;
        }
        return a / b;
    } else {
        return 0;
    }
}

console.log(`10 + 5 = ${calculate(10, 5)}`);
console.log(`10 - 5 = ${calculate(10, 5, "subtract")}`);
console.log(`10 * 5 = ${calculate(10, 5, "multiply")}`);
console.log(`10 / 5 = ${calculate(10, 5, "divide")}`);
console.log(`10 / 0 = ${calculate(10, 0, "divide")}`);

练习 5-8:更多练习

练习 5:编写递归函数 countDown,从 n 倒数到 0,每步打印当前数字。

练习 6:编写函数 formatDeviceInfo,参数为 id: number | string,用重载实现:

  • number 类型:返回 "设备ID: X"
  • string 类型:返回 "设备名称: X"

练习 7:编写 safeDivide 函数,参数为 a: number | string, b: number | string,使用 typeof 判断类型,如果都是数字则返回除法结果,否则返回错误信息字符串。

练习 8:实现递归函数 arraySum,计算多维数组(number[][])中所有数字的和。


附录:ArkTS 函数速查表

// 函数声明
function add(a: number, b: number): number {
    return a + b;
}

// 箭头函数
let add = (a: number, b: number): number => a + b;

// 可选参数
function greet(name: string, greeting?: string): void {
    let msg: string = greeting ?? "Hello";
    console.log(`${msg}, ${name}`);
}

// 默认参数
function power(base: number, exponent: number = 2): number {
    return Math.pow(base, exponent);
}

// 剩余参数
function sum(...numbers: number[]): number {
    let total: number = 0;
    for (let i: number = 0; i < numbers.length; i++) {
        total += numbers[i];
    }
    return total;
}

// 函数重载
function process(x: number): number;
function process(x: string): string;
function process(x: number | string): number | string {
    if (typeof x === 'number') {
        return x * 2;
    } else {
        return x.toUpperCase();
    }
}

// typeof 类型守卫
function handle(value: number | string): void {
    if (typeof value === 'number') {
        console.log(`数字: ${value}`);
    } else {
        console.log(`字符串: ${value}`);
    }
}

// 递归函数
function factorial(n: number): number {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}