TypeScript编程04-函数

TypeScript编程04-函数

文章目录

  • TypeScript编程04-函数
    • [一、箭头函数(Arrow Functions)](#一、箭头函数(Arrow Functions))
      • [1.1 基本语法与特性](#1.1 基本语法与特性)
      • [1.2 箭头函数的 `this` 绑定](#1.2 箭头函数的 this 绑定)
      • [1.3 箭头函数的类型推断](#1.3 箭头函数的类型推断)
    • [二、参数函数(Function Parameters)](#二、参数函数(Function Parameters))
      • [2.1 可选参数与默认参数](#2.1 可选参数与默认参数)
      • [2.2 剩余参数(Rest Parameters)](#2.2 剩余参数(Rest Parameters))
      • [2.3 函数重载(Function Overloads)](#2.3 函数重载(Function Overloads))
      • [2.4 参数解构与命名参数](#2.4 参数解构与命名参数)
    • 三、高级应用与常见问题
      • [3.1 回调函数与类型定义](#3.1 回调函数与类型定义)
      • [3.2 泛型函数](#3.2 泛型函数)
      • [3.3 常见问题排查](#3.3 常见问题排查)
    • 四、最佳实践总结
      • [4.1 箭头函数使用建议](#4.1 箭头函数使用建议)
      • [4.2 参数设计原则](#4.2 参数设计原则)
      • [4.3 类型声明建议](#4.3 类型声明建议)

一、箭头函数(Arrow Functions)

1.1 基本语法与特性

Q1:TypeScript 箭头函数的基本语法是什么?

typescript 复制代码
// 基本语法
const 函数名 = (参数1: 类型, 参数2: 类型): 返回值类型 => {
    // 函数体
    return 结果;
};

// 单行简写(省略 return 和花括号)
const add = (a: number, b: number): number => a + b;

// 无参数
const greet = (): string => "Hello TypeScript";

// 单个参数可省略括号
const square = (x: number): number => x * x;

Q2:箭头函数与普通函数的主要区别是什么?

特性 箭头函数 普通函数
this 绑定 词法作用域(继承外层) 动态作用域(调用时确定)
arguments 对象 没有自己的 arguments 有自己的 arguments
构造函数 不能作为构造函数使用 可以作为构造函数
原型属性 没有 prototype prototype
适用场景 回调函数、方法简写 需要动态 this 的场景

1.2 箭头函数的 this 绑定

Q3:为什么在类方法中使用箭头函数可以避免 this 丢失问题?

typescript 复制代码
class Counter {
    count: number = 0;
    
    // 箭头函数:this 绑定到定义时的上下文(类实例)
    increment = (): void => {
        this.count++; // 这里的 this 始终指向 Counter 实例
    };
    
    // 普通函数:this 取决于调用方式
    decrement(): void {
        this.count--;
    }
}

const counter = new Counter();

// 作为回调传递时
setTimeout(counter.increment, 100); // ✅ 正常工作,this 正确
setTimeout(counter.decrement, 100); // ❌ this 可能丢失,变成 undefined 或 window

Q4:如何在 TypeScript 中正确为类方法绑定 this

typescript 复制代码
class Button {
    text: string = "Click me";
    
    // 方案1:箭头函数属性(推荐用于回调)
    handleClick = (): void => {
        console.log(this.text);
    };
    
    // 方案2:构造函数中绑定
    constructor() {
        this.handleClick2 = this.handleClick2.bind(this);
    }
    handleClick2(): void {
        console.log(this.text);
    }
}

1.3 箭头函数的类型推断

Q5:TypeScript 如何推断箭头函数的返回类型?

typescript 复制代码
// 显式声明返回类型
const multiply = (a: number, b: number): number => a * b;

// 类型推断:TypeScript 自动推断返回类型
const divide = (a: number, b: number) => a / b;  // 推断为 number

// 复杂返回类型建议显式声明
const getUser = (): { id: number; name: string } => ({
    id: 1,
    name: "张三"
});

Q6:如何为箭头函数定义类型别名?

typescript 复制代码
// 定义函数类型别名
type MathOperation = (a: number, b: number) => number;

// 使用类型别名
const add: MathOperation = (x, y) => x + y;
const subtract: MathOperation = (x, y) => x - y;

// 带泛型的函数类型
type Transform<T, R> = (input: T) => R;

const toString: Transform<number, string> = (num) => num.toString();

二、参数函数(Function Parameters)

2.1 可选参数与默认参数

Q7:TypeScript 中如何定义可选参数和默认参数?

typescript 复制代码
// 可选参数(使用 ? 标记)
function greet(name: string, greeting?: string): string {
    if (greeting) {
        return `${greeting}, ${name}!`;
    }
    return `Hello, ${name}!`;
}

// 默认参数
function createUser(
    name: string, 
    age: number = 18, 
    isActive: boolean = true
) {
    return { name, age, isActive };
}

// 调用示例
createUser("张三");           // { name: "张三", age: 18, isActive: true }
createUser("李四", 25);       // { name: "李四", age: 25, isActive: true }
createUser("王五", 30, false); // { name: "王五", age: 30, isActive: false }

Q8:可选参数和默认参数的位置规则是什么?

typescript 复制代码
// ✅ 正确:可选参数在必选参数之后
function func1(a: number, b?: number, c?: number) {}

// ✅ 正确:默认参数可以在任意位置(但建议放在后面)
function func2(a: number, b: number = 10, c?: number) {}

// ❌ 错误:可选参数不能在必选参数前面
// function func3(a?: number, b: number) {} // Error!

// 解决方案:使用默认参数 undefined
function func4(a: number | undefined, b: number) {}

2.2 剩余参数(Rest Parameters)

Q9:如何在 TypeScript 中使用剩余参数?

typescript 复制代码
// 剩余参数必须是数组类型
function sum(...numbers: number[]): number {
    return numbers.reduce((total, num) => total + num, 0);
}

// 与其他参数结合使用
function buildName(firstName: string, ...restNames: string[]): string {
    return [firstName, ...restNames].join(" ");
}

// 调用
sum(1, 2, 3, 4, 5);                    // 15
buildName("张", "三", "丰");            // "张 三 丰"

Q10:剩余参数的类型推断与约束

typescript 复制代码
// 元组类型约束剩余参数
function tupleFunc(...args: [string, number, boolean]): void {
    const [name, age, isActive] = args;
    console.log(name, age, isActive);
}

// 调用必须严格匹配元组结构
tupleFunc("张三", 25, true);  // ✅
// tupleFunc("张三", 25);     // ❌ 参数不足
// tupleFunc("张三", 25, true, "extra"); // ❌ 参数过多

2.3 函数重载(Function Overloads)

Q11:TypeScript 函数重载的基本用法是什么?

typescript 复制代码
// 重载签名(Overload Signatures)
function processInput(input: string): string;
function processInput(input: number): number;
function processInput(input: boolean): boolean;

// 实现签名(Implementation Signature)
function processInput(input: string | number | boolean): string | number | boolean {
    if (typeof input === "string") {
        return input.toUpperCase();
    } else if (typeof input === "number") {
        return input * 2;
    } else {
        return !input;
    }
}

// 调用
const r1 = processInput("hello");  // 类型推断为 string
const r2 = processInput(10);       // 类型推断为 number
const r3 = processInput(true);     // 类型推断为 boolean

Q12:箭头函数如何实现函数重载?

typescript 复制代码
// 箭头函数不能直接重载,需要通过类型别名实现
type StringProcessor = {
    (input: string): string;
    (input: string[]): string[];
};

const processString: StringProcessor = (input: string | string[]) => {
    if (typeof input === "string") {
        return input.toUpperCase();
    }
    return input.map(s => s.toUpperCase());
};

// 或者使用接口
interface NumberProcessor {
    (input: number): number;
    (input: number[]): number[];
}

const processNumber: NumberProcessor = (input) => {
    if (Array.isArray(input)) {
        return input.map(n => n * 2);
    }
    return input * 2;
};

2.4 参数解构与命名参数

Q13:如何在函数参数中使用解构赋值?

typescript 复制代码
// 对象解构
function createUser({ name, age, email }: {
    name: string;
    age: number;
    email: string;
}): void {
    console.log(`用户:${name}, 年龄:${age}, 邮箱:${email}`);
}

// 带默认值的对象解构
function printConfig({ 
    host = "localhost", 
    port = 3000, 
    debug = false 
}: {
    host?: string;
    port?: number;
    debug?: boolean;
}): void {
    console.log(`${host}:${port}, 调试模式:${debug}`);
}

// 数组解构
function processPoint([x, y]: [number, number]): number {
    return Math.sqrt(x ** 2 + y ** 2);
}

Q14:如何实现命名参数模式(Named Parameters)?

typescript 复制代码
// TypeScript 没有原生命名参数,通过对象模拟
interface FetchOptions {
    url: string;
    method?: "GET" | "POST" | "PUT" | "DELETE";
    headers?: Record<string, string>;
    timeout?: number;
}

function fetchData(options: FetchOptions): Promise<any> {
    const { url, method = "GET", headers = {}, timeout = 5000 } = options;
    // 实现逻辑...
    return fetch(url, { method, headers, signal: AbortSignal.timeout(timeout) });
}

// 调用清晰明了,类似命名参数
fetchData({
    url: "/api/users",
    method: "POST",
    timeout: 10000
});

三、高级应用与常见问题

3.1 回调函数与类型定义

Q15:如何正确定义回调函数的类型?

typescript 复制代码
// 定义回调函数类型
type Callback<T> = (result: T, error?: Error) => void;

function fetchUser(id: number, callback: Callback<{ name: string }>): void {
    // 模拟异步操作
    setTimeout(() => {
        if (id > 0) {
            callback({ name: "张三" }, undefined);
        } else {
            callback(undefined as any, new Error("Invalid ID"));
        }
    }, 1000);
}

// 使用
fetchUser(1, (user, err) => {
    if (err) {
        console.error(err);
    } else {
        console.log(user.name);
    }
});

3.2 泛型函数

Q16:如何创建泛型箭头函数?

typescript 复制代码
// 泛型箭头函数
const identity = <T>(arg: T): T => arg;

// 多个泛型参数
const mapPair = <T, U>(a: T, b: U): [T, U] => [a, b];

// 泛型约束
interface HasLength {
    length: number;
}

const logLength = <T extends HasLength>(arg: T): T => {
    console.log(arg.length);
    return arg;
};

// 调用
logLength("hello");     // ✅ string 有 length
logLength([1, 2, 3]);   // ✅ 数组有 length
// logLength(123);      // ❌ number 没有 length

3.3 常见问题排查

Q17:遇到 "Parameter 'x' implicitly has an 'any' type" 错误如何解决?

typescript 复制代码
// ❌ 错误:隐式 any
const badFunc = (x) => x + 1;

// ✅ 解决方案1:显式声明类型
const goodFunc1 = (x: number): number => x + 1;

// ✅ 解决方案2:开启上下文类型推断(在回调中常见)
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2); // TypeScript 自动推断 num 为 number

Q18:如何处理 this 参数的类型声明?

typescript 复制代码
interface User {
    name: string;
    age: number;
}

function greetUser(this: User, greeting: string): string {
    return `${greeting}, ${this.name}!`;
}

// 使用
const user: User = { name: "张三", age: 25 };
const boundGreet = greetUser.bind(user);
boundGreet("你好"); // "你好, 张三!"

// 直接调用会报错
// greetUser("你好"); // Error: 'this' 上下文类型不匹配

四、最佳实践总结

4.1 箭头函数使用建议

场景 推荐方案
简单的回调函数 使用箭头函数,利用类型推断
类中的事件处理 使用箭头函数属性避免 this 绑定问题
需要 arguments 对象 使用普通函数或剩余参数替代
需要动态 this 使用普通函数

4.2 参数设计原则

  1. 必选参数在前,可选参数在后
  2. 默认参数值放在参数列表末尾
  3. 参数过多时(>3个)使用对象解构模拟命名参数
  4. 复杂函数使用重载提高类型安全性

4.3 类型声明建议

typescript 复制代码
// 优先使用类型别名定义函数类型
type Handler<T> = (event: T) => void;

// 复杂函数结构使用接口
interface EventHandlers {
    onClick: (event: MouseEvent) => void;
    onChange: (value: string) => void;
}

// 泛型函数保持简洁,约束条件明确
const pipe = <T, R>(...fns: Array<(arg: T) => T>): ((x: T) => T) => 
    fns.reduce((f, g) => (x) => g(f(x)));

相关推荐
清汤饺子2 小时前
AI 编程新范式:Spec First 的四件套,让 AI 不再是"热情但跑偏的实习生"
前端·javascript·后端
LXXgalaxy2 小时前
小程序文件上传怎么做?一套可复用的 UniApp 上传+预览 Demo
javascript·vue.js·uni-app
Cobyte2 小时前
2.响应式系统基础:依赖追踪的基础 —— 发布订阅模式(前端应用最广的设计模式)
前端·javascript·vue.js
xuankuxiaoyao2 小时前
VUE.JS实践--事件对象和计算属性
javascript·vue.js·ecmascript
朝阳5812 小时前
M3U8 下载助手油猴脚本 - 完全使用指南
前端·javascript·windows
早點睡3902 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-sensors(设备传感器)
javascript·react native·react.js
kadog2 小时前
GraphX:基于 WebGL 区间算术的 GPU 加速隐函数绘图器
前端·javascript·数学建模·webgl
上单带刀不带妹2 小时前
UniApp 页面跳转完全指南:5 种路由方式详解与实战对比
前端·javascript·vue.js·uni-app·跨端开发
Cxiaomu3 小时前
Web 项目的开发/生产环境请求接口配置治理实战
前端·react.js·typescript