TypeScript入门到精通

TypeScript

TypeScript是JavaScript的超集,为这门动态语言添加了静态类型系统。在过去几年里,TypeScript已经从前端社区的"可选项"变成了"默认选择"。大型框架如Angular、React、Vue都使用TypeScript开发,越来越多的企业和项目采用TypeScript。在这篇文章中,我将带你从零开始,全面掌握TypeScript的核心概念和高级特性。

为什么选择TypeScript

JavaScript的动态特性让它灵活强大,但也带来了运行时错误的风险。一个简单的拼写错误可能在代码运行时才被发现,而TypeScript能在编码阶段就发现这些问题。

TypeScript的核心价值:

  • 类型安全:编译时捕获错误,而不是运行时
  • 更好的IDE支持:智能提示、自动完成、重构工具
  • 代码文档化:类型即文档,意图更清晰
  • 更好的协作:团队开发时接口定义明确
  • 渐进式采用:可以逐步迁移现有JavaScript代码

基础类型

原始类型

// 基本类型注解
let name: string = 'TypeScript';
let count: number = 42;
let isActive: boolean = true;

// 空值
let nothing: null = null;
let notDefined: undefined = undefined;

// 任意类型(尽量少用)
let anything: any = 'could be anything';
anything = 123;
anything = true;

数组和元组

// 数组
let numbers: number[] = [1, 2, 3];
let strings: Array = ['a', 'b', 'c'];

// 元组(固定长度和类型的数组)
let tuple: [string, number] = ['age', 25];

// 只读数组
let readonlyNumbers: readonly number[] = [1, 2, 3];

对象类型

// 对象类型注解
let user: { name: string; age: number } = {
  name: 'John',
  age: 30
};

// 可选属性
let config: { host: string; port?: number } = {
  host: 'localhost'
};

接口

接口是TypeScript中定义对象形状的主要方式:

interface User {
  id: number;
  name: string;
  email: string;
  role?: 'admin' | 'user'; // 可选属性
  readonly createdAt: Date; // 只读属性
}

// 接口继承
interface AdminUser extends User {
  permissions: string[];
}

// 函数类型
interface SearchFunc {
  (source: string, subString: string): boolean;
}

类型别名

类型别名为类型创建新名称:

type ID = string | number;
type Point = { x: number; y: number };
type Callback = (data: unknown) => void;

// 联合类型
type Status = 'pending' | 'approved' | 'rejected';

// 交叉类型
type Employee = User & { department: string };

函数类型

// 函数类型注解
function add(a: number, b: number): number {
  return a + b;
}

// 箭头函数
const multiply = (a: number, b: number): number => a * b;

// 默认参数
function greet(name: string = 'World'): string {
  return `Hello, ${name}!`;
}

// 可选参数
function buildName(first: string, last?: string): string {
  return last ? `${first} ${last}` : first;
}

// 函数重载
function format(input: string): string;
function format(input: number): string;
function format(input: string | number): string {
  return String(input);
}

泛型

泛型示意

泛型让我们可以创建可重用的组件,同时保持类型安全:

// 泛型函数
function identity(arg: T): T {
  return arg;
}

let output = identity('hello'); // 类型为 string
let output2 = identity(123); // 类型推断为 number

// 泛型接口
interface Container {
  value: T;
  getValue(): T;
}

// 泛型类
class Box {
  private item: T;
  
  constructor(item: T) {
    this.item = item;
  }
  
  getItem(): T {
    return this.item;
  }
}

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

function logLength(arg: T): T {
  console.log(arg.length);
  return arg;
}

高级类型

联合类型与类型守卫

type StringOrNumber = string | number;

// 类型守卫
function process(value: StringOrNumber) {
  if (typeof value === 'string') {
    // 这里TypeScript知道value是string
    return value.toUpperCase();
  }
  // 这里TypeScript知道value是number
  return value.toFixed(2);
}

// 自定义类型守卫
interface Fish { swim: () => void }
interface Bird { fly: () => void }

function isFish(pet: Fish | Bird): pet is Fish {
  return 'swim' in pet;
}

映射类型

// 将所有属性变为只读
type Readonly = {
  readonly [P in keyof T]: T[P];
};

// 将所有属性变为可选
type Partial = {
  [P in keyof T]?: T[P];
};

// 实用示例
interface User {
  name: string;
  age: number;
}

type ReadonlyUser = Readonly;
type PartialUser = Partial;

条件类型

// 条件类型语法:T extends U ? X : Y
type NonNullable = T extends null | undefined ? never : T;

// infer关键字
type ReturnType = T extends (...args: any[]) => infer R ? R : any;

// 实用:提取函数返回类型
function createUser() { return { name: 'John', age: 30 }; }
type User = ReturnType;

内置工具类型

TypeScript提供了许多实用的工具类型:

interface User {
  id: number;
  name: string;
  email: string;
}

// Partial - 所有属性可选
type PartialUser = Partial;

// Required - 所有属性必需
type RequiredUser = Required;

// Pick - 选择部分属性
type UserName = Pick;

// Omit - 排除部分属性
type UserWithoutEmail = Omit;

// Record - 创建对象类型
type UserMap = Record;

// ReturnType - 获取函数返回类型
type R = ReturnType<() => string>; // string

// Parameters - 获取函数参数类型
type P = Parameters<(a: string, b: number) => void>; // [string, number]

// NonNullable - 排除null和undefined
type NonNull = NonNullable;

模块与命名空间

ES模块

// utils.ts
export function formatDate(date: Date): string {
  return date.toLocaleDateString();
}

export const PI = 3.14159;

// app.ts
import { formatDate, PI } from './utils';

// 默认导出
export default class User {
  constructor(public name: string) {}
}

类型导入

// TypeScript 3.8+ 推荐使用type导入
import type { User } from './types';

// 这在编译后会被完全移除

声明文件

声明文件为JavaScript库提供类型信息:

// my-library.d.ts
declare module 'my-library' {
  export function doSomething(input: string): number;
  export interface Options {
    timeout?: number;
    retries?: number;
  }
  export default class MyLibrary {
    constructor(options?: Options);
    execute(): Promise;
  }
}

TypeScript配置

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

最佳实践

  1. 开启strict模式:获得最完整的类型检查
  2. 避免any:使用unknown代替,或定义更精确的类型
  3. 使用类型推断:不必处处显式标注类型
  4. 优先使用interface:对于对象类型,interface更灵活
  5. 善用工具类型:避免重复定义相似类型
  6. 保持类型定义简单:复杂的类型难以维护

总结

TypeScript是现代前端开发的必备技能。它不仅能帮助你写出更可靠的代码,还能提升开发效率和团队协作质量。从基础类型开始,逐步掌握泛型、高级类型,你会发现TypeScript的强大远超想象。

学习TypeScript最好的方式是在项目中实践。从一个小的JavaScript项目开始,逐步添加类型,你会慢慢体会到类型系统带来的好处。当你习惯了TypeScript的开发体验,你会发现回到纯JavaScript开发竟然会感到不适应。