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"]
}
最佳实践
- 开启strict模式:获得最完整的类型检查
- 避免any:使用unknown代替,或定义更精确的类型
- 使用类型推断:不必处处显式标注类型
- 优先使用interface:对于对象类型,interface更灵活
- 善用工具类型:避免重复定义相似类型
- 保持类型定义简单:复杂的类型难以维护
总结
TypeScript是现代前端开发的必备技能。它不仅能帮助你写出更可靠的代码,还能提升开发效率和团队协作质量。从基础类型开始,逐步掌握泛型、高级类型,你会发现TypeScript的强大远超想象。
学习TypeScript最好的方式是在项目中实践。从一个小的JavaScript项目开始,逐步添加类型,你会慢慢体会到类型系统带来的好处。当你习惯了TypeScript的开发体验,你会发现回到纯JavaScript开发竟然会感到不适应。