摘要函数和对象是 JavaScript 应用中最核心的组成部分。TypeScript 为它们提供了强大的类型系统支持你可以为函数定义参数和返回值类型、实现重载可以用接口Interface清晰描述对象的“形状”让代码更具可读性和可维护性。本文将全面讲解 TypeScript 中的函数类型、可选参数、默认参数、剩余参数、函数重载以及接口的各种用法可选属性、只读属性、索引签名、继承、函数接口等。一、前言在前两篇文章中我们搭建了 TypeScript 环境学习了基础类型number、string、boolean、数组、元组、枚举、any、unknown、void、never以及联合类型、字面量类型等。现在我们可以给简单变量和基本数据结构加上类型了。不过实际项目中我们更多是在函数和对象上使用类型。一个函数是否传对了参数类型一个对象是否包含了应有的字段这些正是 TypeScript 最擅长的场景。二、TypeScript 中的函数类型2.1 函数声明与表达式在 JavaScript 中函数有两种常见定义方式。TypeScript 对两者都提供了类型支持。函数声明Function Declaration// 参数类型和返回值类型都要写 function add(a: number, b: number): number { return a b; }函数表达式Function Expression// 方式一让 TS 自动推导推荐 const subtract function(a: number, b: number): number { return a - b; }; // 方式二显式声明变量类型函数类型 let multiply: (x: number, y: number) number; multiply function(x, y) { return x * y; };这里的(x: number, y: number) number就是函数类型它描述了参数列表和返回值类型。注意箭头不是 ES6 箭头函数而是 TypeScript 的类型表示法。箭头函数写法同理const divide (a: number, b: number): number a / b;2.2 可选参数与默认参数JavaScript 中调用函数时可以少传参数未传的为undefined。TypeScript 中默认要求参数个数匹配但我们可以用?标记可选参数。可选参数必须放在必选参数之后。function greet(name: string, title?: string): string { if (title) { return Hello, ${title} ${name}; } return Hello, ${name}; } console.log(greet(小明)); // Hello, 小明 console.log(greet(小红, Ms.)); // Hello, Ms. 小红默认参数在参数后面赋初始值同时自动变为可选。function createName(first: string, last: string Doe): string { return ${first} ${last}; } console.log(createName(John)); // John Doe console.log(createName(Jane, Smith)); // Jane Smith注意带默认值的参数不一定要放在最后但为了可读性建议最后。2.3 剩余参数Rest Parameters当函数需要接收不定数量的参数时可以使用剩余参数。剩余参数是一个数组需要标注数组类型。function sum(...numbers: number[]): number { return numbers.reduce((total, n) total n, 0); } console.log(sum(1, 2, 3, 4, 5)); // 15也可以使用元组类型来约束剩余参数的类型和数量function logInfo(name: string, ...rest: [number, boolean]): void { console.log(name, rest[0], rest[1]); } logInfo(test, 25, true); // 正确 // logInfo(test, 25); // 错误缺少一个参数 // logInfo(test, 25, true, extra); // 错误多余2.4 函数重载Overloads在 JavaScript 中同一个函数可以因为参数的类型或个数不同而执行不同逻辑。TypeScript 通过重载签名来精确描述这种多态行为。重载的实现定义多个函数类型声明重载签名然后提供一个更宽泛的实现签名。// 重载签名多个 function reverse(str: string): string; function reverse(arr: any[]): any[]; // 实现签名 function reverse(x: string | any[]): string | any[] { if (typeof x string) { return x.split().reverse().join(); } return [...x].reverse(); } console.log(reverse(hello)); // olleh console.log(reverse([1, 2, 3])); // [3, 2, 1]注意重载签名没有函数体只有类型。实现签名需要兼容所有重载签名。调用时根据参数类型匹配最合适的重载签名。三、对象类型对象是 JavaScript 中最常用的数据结构。TypeScript 提供了多种方式描述对象的形状shape。3.1 内联对象类型注解直接在变量或参数后使用{ 属性: 类型 }形式let person: { name: string; age: number } { name: 张三, age: 30 }; function printCoord(pt: { x: number; y: number }) { console.log(坐标: (${pt.x}, ${pt.y})); }当对象类型需要多处复用时内联写法会很冗余这时可以用类型别名或接口。3.2 类型别名中的对象type Point { x: number; y: number; }; function distance(p1: Point, p2: Point): number { return Math.hypot(p1.x - p2.x, p1.y - p2.y); }但接口interface在描述对象时更加强大是 TS 官方推荐的方式之一。四、接口Interface—— 对象的契约接口是 TypeScript 的核心概念之一它定义了一个对象必须包含哪些成员不关心实现细节。可以理解为“合同”或“标准”。4.1 基本接口定义使用interface关键字interface Person { name: string; age: number; } function greetPerson(p: Person): string { return Hello, ${p.name}, you are ${p.age} years old.; } const tom: Person { name: Tom, age: 25 }; console.log(greetPerson(tom));TypeScript 使用结构化类型系统也叫鸭子类型只要对象的结构匹配接口就被认为是兼容的不需要显式implements虽然类可以。4.2 可选属性?与函数可选参数类似对象中的某些属性可以不存在用?标记。interface Product { name: string; price: number; description?: string; // 可选 } const book: Product { name: TS指南, price: 39.9 }; // 合法 const phone: Product { name: 手机, price: 2999, description: 新款 }; // 合法4.3 只读属性readonly属性一旦初始化后就不能修改使用readonly修饰。interface Config { readonly apiUrl: string; timeout: number; } const cfg: Config { apiUrl: https://api.example.com, timeout: 5000 }; cfg.timeout 3000; // ✅ 可以修改 cfg.apiUrl xxx; // ❌ 报错无法分配到 apiUrl因为它是只读属性readonly只是 TypeScript 编译时的检查不影响运行时编译后的 JS 中没有这个限制。4.4 多余属性检查与绕过方法当使用对象字面量直接赋值给接口类型的变量时TypeScript 会进行多余属性检查不能包含接口未定义的属性。interface User { name: string; age: number; } // ❌ 错误对象字面量只能指定已知属性email 不在 User 中 const user: User { name: 李四, age: 28, email: liexample.com };为什么会这样这是为了阻止拼写错误或意外属性污染。三种绕过方法使用类型断言不推荐会绕过所有检查const user { name: 李四, age: 28, email: liexample.com } as User;赋值给中间变量推荐因为中间变量不会触发多余属性检查const temp { name: 李四, age: 28, email: liexample.com }; const user: User temp; // 只检查 name 和 age 是否存在且类型匹配使用索引签名下一节介绍明确允许额外属性。五、高级接口特性5.1 索引签名Index Signatures当你不确定对象有哪些属性名但知道属性值的类型时可以使用索引签名。interface StringMap { [key: string]: string; // 任意属性名值必须是 string } const dict: StringMap { hello: 你好, world: 世界, foo: bar };索引签名可以配合其他属性但其他属性的类型必须兼容索引值的类型或使用联合类型。interface PersonDict { name: string; // 普通属性 age: number; // ❌ 报错number 不能赋给 string因为索引签名要求所有属性值为 string [key: string]: string; // 索引签名 }解决办法是使用联合类型[key: string]: string | number或者把普通属性的类型也包含进去。实际场景动态配置对象、字典数据结构等。5.2 函数接口可调用接口接口不仅可以描述对象还可以描述函数类型。这种接口称为“可调用接口”。interface AddFunction { (a: number, b: number): number; // 调用签名 } const myAdd: AddFunction (x, y) x y;更常见的用途是描述具有调用方法的对象例如装饰器、高阶函数。5.3 接口继承extends接口可以继承一个或多个接口复用定义。interface Animal { name: string; } interface Dog extends Animal { breed: string; } const myDog: Dog { name: 旺财, breed: 金毛 };多继承逗号分隔interface Flyable { fly(): void; } interface Swimmable { swim(): void; } interface Duck extends Flyable, Swimmable { quack(): void; }接口继承与类的继承类似但更灵活可以继承多个。5.4 混合类型接口在 JavaScript 中函数也是对象可以同时拥有属性和方法。接口可以同时包含调用签名和属性签名。interface Counter { (start: number): void; // 可调用 count: number; // 属性 reset(): void; // 方法 } function createCounter(): Counter { let fn function(start: number) { fn.count start; } as Counter; fn.count 0; fn.reset function() { this.count 0; }; return fn; } const c createCounter(); c(5); console.log(c.count); // 5 c.reset(); console.log(c.count); // 0这种模式在库开发中有时会用到。六、接口 vs 类型别名type很多初学者会困惑什么时候用interface什么时候用type。下表总结了主要区别特性interfacetype描述对象✅ 推荐✅ 可以描述联合类型、元组等❌ 不可以✅ 适合继承/扩展extends继承可多继承交叉类型可合并声明合并同名的 interface 会自动合并✅ 支持❌ 不支持类实现implements✅ 可以✅ 可以只要类型结构匹配性能通常更好缓存一般经验法则大多数情况优先用interface因为它更语义化、可扩展。当需要联合类型、元组、映射类型、条件类型等高级特性时用type。// type 适合的场景 type ID string | number; type Point [number, number]; type Callback (data: string) void;七、总结本文全面覆盖了 TypeScript 中的函数与对象类型系统函数部分函数声明与表达式的类型写法可选参数?与默认参数剩余参数...args: type[]函数重载多个签名 一个宽泛实现对象与接口部分内联对象类型、类型别名中的对象接口定义对象形状基本属性、可选属性?、只读属性readonly多余属性检查及三种绕过方式索引签名应对动态属性名函数接口描述可调用的实体接口继承extends实现复用混合类型接口函数对象组合interfacevstype根据场景灵活选择。掌握这些内容后你可以对大多数 JavaScript 代码添加精确的类型描述让编辑器成为你的强力辅助。如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。动手练习是掌握编程最快的方法请务必亲手敲一遍本文的所有示例代码并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源谢谢大家。
TypeScript 从零基础到精通(三):函数、对象与接口
发布时间:2026/6/7 7:03:12
摘要函数和对象是 JavaScript 应用中最核心的组成部分。TypeScript 为它们提供了强大的类型系统支持你可以为函数定义参数和返回值类型、实现重载可以用接口Interface清晰描述对象的“形状”让代码更具可读性和可维护性。本文将全面讲解 TypeScript 中的函数类型、可选参数、默认参数、剩余参数、函数重载以及接口的各种用法可选属性、只读属性、索引签名、继承、函数接口等。一、前言在前两篇文章中我们搭建了 TypeScript 环境学习了基础类型number、string、boolean、数组、元组、枚举、any、unknown、void、never以及联合类型、字面量类型等。现在我们可以给简单变量和基本数据结构加上类型了。不过实际项目中我们更多是在函数和对象上使用类型。一个函数是否传对了参数类型一个对象是否包含了应有的字段这些正是 TypeScript 最擅长的场景。二、TypeScript 中的函数类型2.1 函数声明与表达式在 JavaScript 中函数有两种常见定义方式。TypeScript 对两者都提供了类型支持。函数声明Function Declaration// 参数类型和返回值类型都要写 function add(a: number, b: number): number { return a b; }函数表达式Function Expression// 方式一让 TS 自动推导推荐 const subtract function(a: number, b: number): number { return a - b; }; // 方式二显式声明变量类型函数类型 let multiply: (x: number, y: number) number; multiply function(x, y) { return x * y; };这里的(x: number, y: number) number就是函数类型它描述了参数列表和返回值类型。注意箭头不是 ES6 箭头函数而是 TypeScript 的类型表示法。箭头函数写法同理const divide (a: number, b: number): number a / b;2.2 可选参数与默认参数JavaScript 中调用函数时可以少传参数未传的为undefined。TypeScript 中默认要求参数个数匹配但我们可以用?标记可选参数。可选参数必须放在必选参数之后。function greet(name: string, title?: string): string { if (title) { return Hello, ${title} ${name}; } return Hello, ${name}; } console.log(greet(小明)); // Hello, 小明 console.log(greet(小红, Ms.)); // Hello, Ms. 小红默认参数在参数后面赋初始值同时自动变为可选。function createName(first: string, last: string Doe): string { return ${first} ${last}; } console.log(createName(John)); // John Doe console.log(createName(Jane, Smith)); // Jane Smith注意带默认值的参数不一定要放在最后但为了可读性建议最后。2.3 剩余参数Rest Parameters当函数需要接收不定数量的参数时可以使用剩余参数。剩余参数是一个数组需要标注数组类型。function sum(...numbers: number[]): number { return numbers.reduce((total, n) total n, 0); } console.log(sum(1, 2, 3, 4, 5)); // 15也可以使用元组类型来约束剩余参数的类型和数量function logInfo(name: string, ...rest: [number, boolean]): void { console.log(name, rest[0], rest[1]); } logInfo(test, 25, true); // 正确 // logInfo(test, 25); // 错误缺少一个参数 // logInfo(test, 25, true, extra); // 错误多余2.4 函数重载Overloads在 JavaScript 中同一个函数可以因为参数的类型或个数不同而执行不同逻辑。TypeScript 通过重载签名来精确描述这种多态行为。重载的实现定义多个函数类型声明重载签名然后提供一个更宽泛的实现签名。// 重载签名多个 function reverse(str: string): string; function reverse(arr: any[]): any[]; // 实现签名 function reverse(x: string | any[]): string | any[] { if (typeof x string) { return x.split().reverse().join(); } return [...x].reverse(); } console.log(reverse(hello)); // olleh console.log(reverse([1, 2, 3])); // [3, 2, 1]注意重载签名没有函数体只有类型。实现签名需要兼容所有重载签名。调用时根据参数类型匹配最合适的重载签名。三、对象类型对象是 JavaScript 中最常用的数据结构。TypeScript 提供了多种方式描述对象的形状shape。3.1 内联对象类型注解直接在变量或参数后使用{ 属性: 类型 }形式let person: { name: string; age: number } { name: 张三, age: 30 }; function printCoord(pt: { x: number; y: number }) { console.log(坐标: (${pt.x}, ${pt.y})); }当对象类型需要多处复用时内联写法会很冗余这时可以用类型别名或接口。3.2 类型别名中的对象type Point { x: number; y: number; }; function distance(p1: Point, p2: Point): number { return Math.hypot(p1.x - p2.x, p1.y - p2.y); }但接口interface在描述对象时更加强大是 TS 官方推荐的方式之一。四、接口Interface—— 对象的契约接口是 TypeScript 的核心概念之一它定义了一个对象必须包含哪些成员不关心实现细节。可以理解为“合同”或“标准”。4.1 基本接口定义使用interface关键字interface Person { name: string; age: number; } function greetPerson(p: Person): string { return Hello, ${p.name}, you are ${p.age} years old.; } const tom: Person { name: Tom, age: 25 }; console.log(greetPerson(tom));TypeScript 使用结构化类型系统也叫鸭子类型只要对象的结构匹配接口就被认为是兼容的不需要显式implements虽然类可以。4.2 可选属性?与函数可选参数类似对象中的某些属性可以不存在用?标记。interface Product { name: string; price: number; description?: string; // 可选 } const book: Product { name: TS指南, price: 39.9 }; // 合法 const phone: Product { name: 手机, price: 2999, description: 新款 }; // 合法4.3 只读属性readonly属性一旦初始化后就不能修改使用readonly修饰。interface Config { readonly apiUrl: string; timeout: number; } const cfg: Config { apiUrl: https://api.example.com, timeout: 5000 }; cfg.timeout 3000; // ✅ 可以修改 cfg.apiUrl xxx; // ❌ 报错无法分配到 apiUrl因为它是只读属性readonly只是 TypeScript 编译时的检查不影响运行时编译后的 JS 中没有这个限制。4.4 多余属性检查与绕过方法当使用对象字面量直接赋值给接口类型的变量时TypeScript 会进行多余属性检查不能包含接口未定义的属性。interface User { name: string; age: number; } // ❌ 错误对象字面量只能指定已知属性email 不在 User 中 const user: User { name: 李四, age: 28, email: liexample.com };为什么会这样这是为了阻止拼写错误或意外属性污染。三种绕过方法使用类型断言不推荐会绕过所有检查const user { name: 李四, age: 28, email: liexample.com } as User;赋值给中间变量推荐因为中间变量不会触发多余属性检查const temp { name: 李四, age: 28, email: liexample.com }; const user: User temp; // 只检查 name 和 age 是否存在且类型匹配使用索引签名下一节介绍明确允许额外属性。五、高级接口特性5.1 索引签名Index Signatures当你不确定对象有哪些属性名但知道属性值的类型时可以使用索引签名。interface StringMap { [key: string]: string; // 任意属性名值必须是 string } const dict: StringMap { hello: 你好, world: 世界, foo: bar };索引签名可以配合其他属性但其他属性的类型必须兼容索引值的类型或使用联合类型。interface PersonDict { name: string; // 普通属性 age: number; // ❌ 报错number 不能赋给 string因为索引签名要求所有属性值为 string [key: string]: string; // 索引签名 }解决办法是使用联合类型[key: string]: string | number或者把普通属性的类型也包含进去。实际场景动态配置对象、字典数据结构等。5.2 函数接口可调用接口接口不仅可以描述对象还可以描述函数类型。这种接口称为“可调用接口”。interface AddFunction { (a: number, b: number): number; // 调用签名 } const myAdd: AddFunction (x, y) x y;更常见的用途是描述具有调用方法的对象例如装饰器、高阶函数。5.3 接口继承extends接口可以继承一个或多个接口复用定义。interface Animal { name: string; } interface Dog extends Animal { breed: string; } const myDog: Dog { name: 旺财, breed: 金毛 };多继承逗号分隔interface Flyable { fly(): void; } interface Swimmable { swim(): void; } interface Duck extends Flyable, Swimmable { quack(): void; }接口继承与类的继承类似但更灵活可以继承多个。5.4 混合类型接口在 JavaScript 中函数也是对象可以同时拥有属性和方法。接口可以同时包含调用签名和属性签名。interface Counter { (start: number): void; // 可调用 count: number; // 属性 reset(): void; // 方法 } function createCounter(): Counter { let fn function(start: number) { fn.count start; } as Counter; fn.count 0; fn.reset function() { this.count 0; }; return fn; } const c createCounter(); c(5); console.log(c.count); // 5 c.reset(); console.log(c.count); // 0这种模式在库开发中有时会用到。六、接口 vs 类型别名type很多初学者会困惑什么时候用interface什么时候用type。下表总结了主要区别特性interfacetype描述对象✅ 推荐✅ 可以描述联合类型、元组等❌ 不可以✅ 适合继承/扩展extends继承可多继承交叉类型可合并声明合并同名的 interface 会自动合并✅ 支持❌ 不支持类实现implements✅ 可以✅ 可以只要类型结构匹配性能通常更好缓存一般经验法则大多数情况优先用interface因为它更语义化、可扩展。当需要联合类型、元组、映射类型、条件类型等高级特性时用type。// type 适合的场景 type ID string | number; type Point [number, number]; type Callback (data: string) void;七、总结本文全面覆盖了 TypeScript 中的函数与对象类型系统函数部分函数声明与表达式的类型写法可选参数?与默认参数剩余参数...args: type[]函数重载多个签名 一个宽泛实现对象与接口部分内联对象类型、类型别名中的对象接口定义对象形状基本属性、可选属性?、只读属性readonly多余属性检查及三种绕过方式索引签名应对动态属性名函数接口描述可调用的实体接口继承extends实现复用混合类型接口函数对象组合interfacevstype根据场景灵活选择。掌握这些内容后你可以对大多数 JavaScript 代码添加精确的类型描述让编辑器成为你的强力辅助。如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。动手练习是掌握编程最快的方法请务必亲手敲一遍本文的所有示例代码并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源谢谢大家。