前言
大家好,我是小杨。记得我刚从JavaScript转向TypeScript时,最让我惊喜的就是函数的类型安全特性。以前在JavaScript中,我经常遇到"undefined is not a function"这样的运行时错误,而TypeScript的函数特性就像给代码系上了安全带,让很多错误在编写阶段就能被发现。今天就来聊聊TypeScript中的函数,以及它和JavaScript函数的那些区别。
基础篇:TypeScript函数的基本用法
函数声明的类型化
在JavaScript中,我们这样写函数:
typescript
// JavaScript风格
function greet(name) {
return `Hello, ${name}!`;
}
而在TypeScript中,我们可以为参数和返回值添加类型:
typescript
// TypeScript风格 - 为函数系上安全带
function greet(name: string): string {
return `Hello, ${name}!`;
}
// 使用示例
const message = greet("Alice"); // ✅ 正确
// const errorMessage = greet(123); // ❌ 编译错误:参数类型不匹配
函数表达式与箭头函数
typescript
// 函数表达式
const add = function(x: number, y: number): number {
return x + y;
};
// 箭头函数 - 我的最爱
const multiply = (x: number, y: number): number => x * y;
// 使用类型别名
type MathOperation = (a: number, b: number) => number;
const divide: MathOperation = (a, b) => a / b;
进阶特性:TypeScript函数的超能力
1. 可选参数和默认参数
typescript
// 可选参数
function createUser(
name: string,
email: string,
age?: number // 这个问号让参数变成可选的
): User {
return {
name,
email,
age: age || 0 // 处理可选参数
};
}
// 默认参数
function sendMessage(
content: string,
priority: "low" | "medium" | "high" = "medium", // 默认值
timeout: number = 5000
): void {
console.log(`Sending: ${content}, Priority: ${priority}, Timeout: ${timeout}ms`);
}
// 使用示例
createUser("Alice", "alice@example.com"); // ✅ age是可选的
createUser("Bob", "bob@example.com", 25); // ✅ 也可以提供age
sendMessage("Hello"); // ✅ 使用默认参数
sendMessage("Urgent!", "high", 1000); // ✅ 自定义所有参数
2. 剩余参数
typescript
// 收集所有参数到一个数组中
function buildPath(...segments: string[]): string {
return segments.join('/');
}
// 混合使用
function configureApp(
name: string,
...settings: [string, any][]
): AppConfig {
console.log(`Configuring app: ${name}`);
settings.forEach(([key, value]) => {
console.log(`Setting ${key} to ${value}`);
});
return { name, settings };
}
// 使用示例
const path = buildPath("usr", "local", "bin", "app"); // "usr/local/bin/app"
configureApp("MyApp", ["theme", "dark"], ["lang", "zh-CN"]);
3. 函数重载
这是TypeScript独有的强大特性:
typescript
// 重载签名
function processInput(input: string): string[];
function processInput(input: number): number[];
function processInput(input: boolean): boolean[];
// 实现签名
function processInput(input: any): any[] {
if (typeof input === 'string') {
return input.split('');
} else if (typeof input === 'number') {
return [input, input * 2, input * 3];
} else {
return [input, !input];
}
}
// 使用示例 - 自动获得正确的类型提示!
const chars = processInput("hello"); // string[] 类型
const numbers = processInput(5); // number[] 类型
const booleans = processInput(true); // boolean[] 类型
实战对比:TypeScript vs JavaScript函数
场景1:API请求函数
JavaScript版本:
typescript
// JavaScript - 容易出错
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
return data; // 返回什么?我们不知道!
}
// 使用时可能会遇到问题
const userData = await fetchUserData(123);
console.log(userData.nonExistentProperty); // 运行时才报错!
TypeScript版本:
typescript
// TypeScript - 安全明确
interface User {
id: number;
name: string;
email: string;
avatar?: string;
}
async function fetchUserData(userId: number): Promise<User> {
const response = await fetch(`/api/users/${userId}`);
const data: User = await response.json();
return data; // 明确的返回类型
}
// 使用时获得完整的类型安全
const userData = await fetchUserData(123);
console.log(userData.name); // ✅ 正确的属性
// console.log(userData.nonExistentProperty); // ❌ 编译时就报错!
场景2:回调函数处理
JavaScript版本:
typescript
// JavaScript - 容易出错
function processArray(arr, callback) {
const result = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i]));
}
return result;
}
// 可能出现的错误
const numbers = [1, 2, 3];
const doubled = processArray(numbers, (num) => num * 2); // 正常工作
const problematic = processArray(numbers, "not a function"); // 运行时崩溃!
TypeScript版本:
typescript
// TypeScript - 类型安全
function processArray<T, U>(
arr: T[],
callback: (item: T, index: number) => U
): U[] {
const result: U[] = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i], i));
}
return result;
}
// 使用示例 - 完全类型安全
const numbers = [1, 2, 3];
const doubled = processArray(numbers, (num) => num * 2); // number[] 类型
const strings = processArray(numbers, (num) => num.toString()); // string[] 类型
// const error = processArray(numbers, "not a function"); // ❌ 编译错误
场景3:配置对象处理
JavaScript版本:
typescript
// JavaScript - 配置容易出错
function createButton(config) {
const defaultConfig = {
text: "Button",
color: "blue",
size: "medium",
disabled: false
};
return { ...defaultConfig, ...config };
}
// 可能的问题
const button1 = createButton({ text: "Click me", colour: "red" }); // 拼写错误,静默失败
const button2 = createButton({ size: "extra-large" }); // 无效的尺寸,运行时才可能发现
TypeScript版本:
typescript
// TypeScript - 配置安全
interface ButtonConfig {
text?: string;
color?: "blue" | "red" | "green" | "yellow";
size?: "small" | "medium" | "large";
disabled?: boolean;
}
function createButton(config: ButtonConfig) {
const defaultConfig: Required<ButtonConfig> = {
text: "Button",
color: "blue",
size: "medium",
disabled: false
};
return { ...defaultConfig, ...config };
}
// 使用示例 - 即时错误检测
const button1 = createButton({
text: "Click me",
color: "red", // ✅ 有效颜色
size: "large" // ✅ 有效尺寸
});
// const button2 = createButton({ colour: "red" }); // ❌ 拼写错误,编译时报错
// const button3 = createButton({ size: "extra-large" }); // ❌ 无效尺寸,编译时报错
高级特性:TypeScript函数的独特能力
1. 泛型函数
typescript
// 泛型让函数更加灵活
function identity<T>(value: T): T {
return value;
}
// 自动类型推断
const num = identity(42); // number 类型
const str = identity("hello"); // string 类型
const bool = identity(true); // boolean 类型
// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 30 };
const userName = getProperty(user, "name"); // string 类型
const userAge = getProperty(user, "age"); // number 类型
// const invalid = getProperty(user, "email"); // ❌ 编译错误
2. 条件类型与函数
typescript
// 基于输入类型的条件返回
type ApiResponse<T> = T extends number
? { data: number; type: "number" }
: T extends string
? { data: string; type: "string" }
: { data: T; type: "object" };
function createResponse<T>(data: T): ApiResponse<T> {
if (typeof data === "number") {
return { data, type: "number" } as ApiResponse<T>;
} else if (typeof data === "string") {
return { data, type: "string" } as ApiResponse<T>;
} else {
return { data, type: "object" } as ApiResponse<T>;
}
}
// 自动推断正确的返回类型
const numResponse = createResponse(42); // { data: number; type: "number" }
const strResponse = createResponse("hello"); // { data: string; type: "string" }
3. 函数类型的高级用法
typescript
// 函数组合
type FunctionType<T, R> = (arg: T) => R;
function compose<T, U, R>(
f: FunctionType<U, R>,
g: FunctionType<T, U>
): FunctionType<T, R> {
return (x: T) => f(g(x));
}
// 使用组合
const addFive = (x: number) => x + 5;
const multiplyByTwo = (x: number) => x * 2;
const toString = (x: number) => x.toString();
const processNumber = compose(toString, compose(addFive, multiplyByTwo));
const result = processNumber(10); // "25"
最佳实践和注意事项
1. 合理使用any和unknown
typescript
// 不推荐:过度使用any
function dangerousFunction(data: any): any {
// 这里可能发生任何事!
return data.someProperty.someMethod();
}
// 推荐:使用unknown进行类型安全处理
function safeFunction(data: unknown): string {
if (typeof data === 'string') {
return data.toUpperCase();
} else if (data && typeof data === 'object' && 'message' in data) {
return String(data.message);
}
return "Unknown data";
}
2. 充分利用类型推断
typescript
// TypeScript可以推断返回类型,不需要总是显式声明
function calculateTotal(price: number, quantity: number) {
return price * quantity; // 自动推断返回number类型
}
// 但对于复杂逻辑,显式声明更好
function processOrder(order: Order): ProcessResult {
// 复杂的处理逻辑...
return result;
}
3. 错误处理的最佳实践
typescript
// 使用Result模式而不是抛出错误
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function safeDivide(a: number, b: number): Result<number> {
if (b === 0) {
return { success: false, error: new Error("Division by zero") };
}
return { success: true, data: a / b };
}
// 使用示例
const result = safeDivide(10, 2);
if (result.success) {
console.log(result.data); // 5
} else {
console.error(result.error.message);
}
总结:TypeScript函数的优势
通过上面的对比和实践,我们可以看到TypeScript函数相比JavaScript函数的主要优势:
- 类型安全:在编译时捕获类型错误
- 更好的智能提示:IDE可以提供准确的参数和返回类型提示
- 自文档化:函数签名本身就是很好的文档
- 重构友好:类型系统帮助安全地进行代码重构
- 团队协作:明确的接口约定减少沟通成本
结语
从JavaScript的"自由奔放"到TypeScript的"规范有序",函数的类型化可能是最有价值的改进之一。它就像给我们的代码加上了安全带,虽然一开始可能觉得有些束缚,但一旦习惯,就会发现它能避免很多潜在的事故。
记住,好的TypeScript代码不是一味地添加类型,而是找到类型安全和开发效率的最佳平衡点。希望今天的分享能帮助你在实际项目中更好地使用TypeScript函数!
⭐ 写在最后
请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式
✅ 认为我部分代码过于老旧,可以提供新的API或最新语法
✅ 对于文章中部分内容不理解
✅ 解答我文章中一些疑问
✅ 认为某些交互,功能需要优化,发现BUG
✅ 想要添加新功能,对于整体的设计,外观有更好的建议
✅ 一起探讨技术加qq交流群:906392632
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!