TypeScript 类型定义:变量与函数的类型注解

97 阅读4分钟

掌握 TypeScript 的核心技能是从正确声明变量和函数类型开始的。类型注解不仅能帮助开发者在编码阶段捕获错误,还能作为代码自文档化的有效工具,显著提升代码的可读性和可维护性。本文将深入探讨如何高效地为变量和函数添加类型注解。

一、变量类型定义

1. 基本类型注解

TypeScript 提供了一套基本类型系统,涵盖了 JavaScript 的基础数据类型:

// 字符串类型
let username: string = "Alice";

// 数字类型
let userAge: number = 30;

// 布尔类型
let isAdmin: boolean = false;

// 数组类型
let numbers: number[] = [1, 2, 3];
let colors: Array<string> = ["red", "green", "blue"];

// 元组类型 - 固定长度和类型的数组
let userInfo: [string, number] = ["Bob", 25];

2. 类型推断的优势

TypeScript 能够自动推断变量类型,减少冗余注解:

// 类型推断:自动确定为 string
let message = "Hello, TypeScript!";

// 类型推断:自动确定为 number[]
const numbers = [1, 2, 3]; 

// 类型推断:自动确定为 (string | number)[]
const mixedArray = ["text", 42, true]; 

3. 高级变量类型技巧

可空类型与默认值:

// 可选类型
let optionalValue: string | undefined;

// 使用默认值处理可选类型
function greet(name: string | undefined = "Guest") {
  return `Hello, ${name}!`;
}

只读限定:

// 只读数组
const readOnlyNames: ReadonlyArray<string> = ["Alice", "Bob"];
// readOnlyNames.push("Charlie"); // 错误:只读数组不可修改

// 只读对象属性
interface Config {
  readonly apiUrl: string;
}

常量断言:

// 常量断言 - 锁定类型和值
const routes = ["/home", "/about", "/contact"] as const;
// routes[0] = "/dashboard"; // 错误:只读属性
// 类型为:readonly ["/home", "/about", "/contact"]

二、函数类型定义

1. 函数声明与表达式

命名函数:

// 带类型注解的函数
function add(x: number, y: number): number {
  return x + y;
}

// 可选参数
function createUser(name: string, age?: number): void {
  console.log(`Name: ${name}, Age: ${age || "unknown"}`);
}

函数表达式:

// 函数表达式
const multiply = function(x: number, y: number): number {
  return x * y;
};

// 箭头函数
const divide = (x: number, y: number): number => x / y;

2. 函数类型的高级特性

可选参数与默认值:

// 带默认值的参数
function greet(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`;
}

// 剩余参数
function sum(...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0);
}

函数重载:

// 函数重载声明
function getUserInfo(id: string): User | undefined;
function getUserInfo(email: string): User | undefined;
function getUserInfo(input: string): User | undefined {
  // 实现细节...
  return users.find(u => u.id === input || u.email === input);
}

// 使用重载函数
const userById = getUserInfo("user-123");
const userByEmail = getUserInfo("alice@example.com");

3. 函数类型作为一等公民

类型化函数变量:

// 定义函数类型
type MathOperation = (x: number, y: number) => number;

// 使用函数类型
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;

接口定义函数类型:

interface SearchFunction {
  (source: string, keyword: string): boolean;
}

const search: SearchFunction = (src, kw) => src.includes(kw);

三、对象类型与解构的类型注解

1. 对象类型定义

内联类型注解:

// 内联对象类型
const user: { name: string; age: number; isAdmin?: boolean } = {
  name: "Alice",
  age: 30
};

接口定义对象:

// 使用接口
interface User {
  id: number;
  name: string;
  email: string;
}

const currentUser: User = {
  id: 1,
  name: "Bob",
  email: "bob@example.com"
};

2. 解构的类型注解

函数参数解构:

// 对象解构参数
function printUser({ name, age }: { name: string; age: number }): void {
  console.log(`${name}, ${age} years old`);
}

// 配合接口更清晰
interface UserInfo {
  name: string;
  age: number;
}

function displayUser({ name, age }: UserInfo): void {
  console.log(`${name}, ${age}`);
}

数组解构:

// 数组解构
const rgb: [number, number, number] = [255, 128, 64];
const [red, green, blue] = rgb;

// 元组类型保证安全
function getCoordinates(): [number, number] {
  return [40.7128, -74.0060]; // 返回经纬度
}
const [latitude, longitude] = getCoordinates();

四、实用类型技巧与模式

1. 类型组合技巧

联合与交叉类型:

// 联合类型
type UserId = string | number;

// 交叉类型
interface Name {
  firstName: string;
  lastName: string;
}

interface Contact {
  email: string;
  phone: string;
}

type Customer = Name & Contact;

类型断言:

// 在开发者确定类型时使用
const input = document.getElementById("username") as HTMLInputElement;
// 或使用泛型语法
const emailInput = <HTMLInputElement>document.getElementById("email");

2. 复杂函数签名

泛型函数:

// 泛型函数 - 处理多种类型
function identity<T>(arg: T): T {
  return arg;
}

// 使用
const num = identity<number>(42);
const str = identity("hello"); // 类型推断

函数工厂模式:

// 创建特定类型函数的工厂
function createLogger<T>(): (item: T) => void {
  return (item: T) => console.log(item);
}

const numberLogger = createLogger<number>();
numberLogger(42); // 输出:42

五、最佳实践与常见陷阱

1. 类型定义最佳实践

  • 优先类型推断:让 TypeScript 自动推断类型,减少冗余注解
  • 使用类型别名和接口:提升复杂类型的可重用性
  • 避免过度使用 any:优先考虑 unknown 或具体类型
  • 启用严格模式:在 tsconfig.json 中设置 "strict": true

2. 常见陷阱与解决方案

常见问题问题描述解决方案
可选参数位置必须在必需参数后function(a: number, b?: string)
函数重载混乱实现签名太宽泛明确声明每个重载签名
对象类型冗余重复定义对象结构使用接口或类型别名
过度类型断言滥用 as 操作符重构代码使类型自然匹配
any 类型扩散污染类型系统局部使用并尽快转为具体类型

3. 函数类型检查技巧

// 参数类型检查
function processInput(input: unknown): void {
  if (typeof input === "string") {
    // 处理字符串
  } else if (Array.isArray(input)) {
    // 处理数组
  }
}

// 类型谓词
interface Cat {
  meow(): void;
}

function isCat(pet: any): pet is Cat {
  return pet && typeof pet.meow === "function";
}

if (isCat(somePet)) {
  somePet.meow(); // 安全调用
}

掌握类型,掌握代码

TypeScript 的类型系统是构建健壮应用的重要保障。通过实践本指南中的技术与模式,您将能够:

  1. 编写自文档化代码:清晰的类型注解使代码意图更明确
  2. 提前捕获错误:在编译阶段而非运行时发现类型问题
  3. 提高开发体验:编辑器智能提示加速开发流程
  4. 促进团队协作:统一的类型约定减少沟通成本
  5. 构建可靠系统:降低生产环境中的类型相关错误

“类型注解不是负担,而是开发者与编译器之间的协作契约 - 它定义了代码行为应遵守的规则。”

通过持续实践这些类型定义技巧,您将发现 TypeScript 真正强大的地方不只是在类型检查本身,更在于它能帮助您以更严谨、更可靠的方式思考问题解决路径。