TypeScript 从入门到精通:完整教程与实战应用(一)
1. TypeScript 简介
什么是 TypeScript?
TypeScript 是 JavaScript 的超集,添加了静态类型系统,由微软开发并开源。它编译成纯 JavaScript 运行在任何 JavaScript 环境中。
为什么使用 TypeScript?
-
类型安全:在编译时捕获错误
-
更好的开发体验:智能提示和代码补全
-
更容易维护:清晰的代码结构和接口定义
-
渐进式采用:可以逐步将 JS 项目迁移到 TS
2. 环境搭建
安装:
npm install -g typescript
验证安装:
tsc --version
初始化项目:
mkdir ts-project
cd ts-project
npm init -y
tsc --init
3. 基础类型
TypeScript 提供了多种基本类型:
// 布尔值
let isDone: boolean = false;// 数字
let decimal: number = 6;
let hex: number = 0xf00d;// 字符串
let color: string = "blue";// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型语法// 元组
let tuple: [string, number] = ["hello", 10];// 枚举
enum Color {Red, Green, Blue}
let c: Color = Color.Green;// Any
let notSure: any = 4;
notSure = "maybe a string instead";// Void
function warnUser(): void {console.log("This is a warning message");
}// Null 和 Undefined
let u: undefined = undefined;
let n: null = null;// Never
function error(message: string): never {throw new Error(message);
}// 类型断言
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
4. 接口 (Interfaces)
接口是 TypeScript 的核心概念之一:
interface Person {firstName: string;lastName: string;age?: number; // 可选属性readonly id: number; // 只读属性[propName: string]: any; // 任意属性
}function greet(person: Person) {return `Hello, ${person.firstName} ${person.lastName}`;
}let user = { firstName: "Jane", lastName: "User", id: 12345 };
console.log(greet(user));
5. 类 (Classes)
TypeScript 提供了完整的类实现:
class Animal {private name: string;constructor(name: string) {this.name = name;}move(distance: number = 0) {console.log(`${this.name} moved ${distance}m.`);}
}class Snake extends Animal {constructor(name: string) {super(name);}move(distance: number = 5) {console.log("Slithering...");super.move(distance);}
}let sam = new Snake("Sammy");
sam.move();
第二部分:TypeScript 进阶 (30-70%)
6. 泛型 (Generics)
泛型提供了代码复用的强大方式:
function identity<T>(arg: T): T {return arg;
}let output = identity<string>("myString");
let output2 = identity("myString"); // 类型推断// 泛型接口
interface GenericIdentityFn<T> {(arg: T): T;
}let myIdentity: GenericIdentityFn<number> = identity;// 泛型类
class GenericNumber<T> {zeroValue: T;add: (x: T, y: T) => T;
}let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
7. 高级类型
联合类型:
let padding: string | number;
类型别名:
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
字符串字面量类型:
type Easing = "ease-in" | "ease-out" | "ease-in-out";
可辨识联合:
interface Square {kind: "square";size: number;
}interface Rectangle {kind: "rectangle";width: number;height: number;
}type Shape = Square | Rectangle;function area(s: Shape) {switch (s.kind) {case "square": return s.size * s.size;case "rectangle": return s.height * s.width;}
}
8. 模块和命名空间
模块:
// math.ts
export function add(x: number, y: number): number {return x + y;
}// app.ts
import { add } from './math';
console.log(add(1, 2));
命名空间:
namespace Validation {export interface StringValidator {isAcceptable(s: string): boolean;}const lettersRegexp = /^[A-Za-z]+$/;export class LettersOnlyValidator implements StringValidator {isAcceptable(s: string) {return lettersRegexp.test(s);}}
}let validators: { [s: string]: Validation.StringValidator } = {};
validators["Letters only"] = new Validation.LettersOnlyValidator();
9. 装饰器 (Decorators)
装饰器是一种特殊类型的声明:
function sealed(constructor: Function) {Object.seal(constructor);Object.seal(constructor.prototype);
}@sealed
class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}greet() {return "Hello, " + this.greeting;}
}
第三部分:TypeScript 实战 (70-100%)
10. 与前端框架集成
React + TypeScript:
import React, { useState } from 'react';interface Props {name: string;age?: number;
}const Greeting: React.FC<Props> = ({ name, age = 18 }) => {const [count, setCount] = useState<number>(0);return (<div><h1>Hello {name}, age {age}</h1><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
};export default Greeting;
Vue + TypeScript:
<template><div><h1>Hello {{ name }}, age {{ age }}</h1><p>You clicked {{ count }} times</p><button @click="increment">Click me</button></div>
</template><script lang="ts">
import { defineComponent, ref } from 'vue';export default defineComponent({props: {name: {type: String,required: true},age: {type: Number,default: 18}},setup(props) {const count = ref(0);function increment() {count.value++;}return {count,increment};}
});
</script>
11. Node.js 后端开发
Express + TypeScript:
import express, { Request, Response } from 'express';interface User {id: number;name: string;
}const app = express();
app.use(express.json());const users: User[] = [{ id: 1, name: 'Alice' },{ id: 2, name: 'Bob' }
];app.get('/users', (req: Request, res: Response<User[]>) => {res.json(users);
});app.post('/users', (req: Request<{}, {}, User>, res: Response<User>) => {const newUser = req.body;users.push(newUser);res.status(201).json(newUser);
});const PORT = 3000;
app.listen(PORT, () => {console.log(`Server running on http://localhost:${PORT}`);
});
12. 测试与调试
Jest + TypeScript:
// math.test.ts
import { add, subtract } from './math';describe('math functions', () => {it('should add two numbers', () => {expect(add(1, 2)).toBe(3);});it('should subtract two numbers', () => {expect(subtract(5, 3)).toBe(2);});
});// math.ts
export function add(a: number, b: number): number {return a + b;
}export function subtract(a: number, b: number): number {return a - b;
}
13. 高级配置与优化
tsconfig.json 详解:
{"compilerOptions": {"target": "es6", // 编译目标版本"module": "commonjs", // 模块系统"strict": true, // 启用所有严格类型检查选项"esModuleInterop": true, // 兼容 CommonJS 和 ES Modules"skipLibCheck": true, // 跳过声明文件的类型检查"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致"outDir": "./dist", // 输出目录"rootDir": "./src", // 源文件目录"baseUrl": ".", // 解析非相对模块的基础目录"paths": { // 路径映射"@/*": ["src/*"]},"declaration": true, // 生成 .d.ts 声明文件"sourceMap": true // 生成 source map},"include": ["src/**/*"],"exclude": ["node_modules", "**/*.spec.ts"]
}
14. 性能优化与最佳实践
-
使用 const 断言:
const colors = ["red", "green", "blue"] as const;
-
避免 any 类型:
// 不好 function logValue(val: any) {console.log(val); }// 好 function logValue<T>(val: T) {console.log(val); }
-
使用类型守卫:
function isString(test: any): test is string {return typeof test === "string"; }
-
合理使用 unknown 类型:
function safeParse(json: string): unknown {return JSON.parse(json); }
-
使用实用类型:
interface User {id: number;name: string;age: number; }type PartialUser = Partial<User>; // 所有属性可选 type ReadonlyUser = Readonly<User>; // 所有属性只读 type UserWithoutAge = Omit<User, 'age'>; // 排除 age 属性
第四部分:项目实战
15. 完整项目:Todo 应用
项目结构:
todo-app/
├── src/
│ ├── components/
│ │ ├── TodoItem.tsx
│ │ └── TodoList.tsx
│ ├── models/
│ │ └── todo.ts
│ ├── App.tsx
│ └── index.tsx
├── public/
├── tsconfig.json
└── package.json
核心代码:
// models/todo.ts
export interface Todo {id: number;text: string;completed: boolean;
}export type TodoFilter = 'all' | 'active' | 'completed';
// components/TodoItem.tsx
import React from 'react';interface Props {todo: Todo;onToggle: (id: number) => void;onDelete: (id: number) => void;
}const TodoItem: React.FC<Props> = ({ todo, onToggle, onDelete }) => {return (<div><inputtype="checkbox"checked={todo.completed}onChange={() => onToggle(todo.id)}/><span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>{todo.text}</span><button onClick={() => onDelete(todo.id)}>Delete</button></div>);
};export default TodoItem;
// components/TodoList.tsx
import React, { useState } from 'react';
import TodoItem from './TodoItem';
import { Todo, TodoFilter } from '../models/todo';interface Props {todos: Todo[];onToggle: (id: number) => void;onDelete: (id: number) => void;
}const TodoList: React.FC<Props> = ({ todos, onToggle, onDelete }) => {const [filter, setFilter] = useState<TodoFilter>('all');const filteredTodos = todos.filter(todo => {if (filter === 'active') return !todo.completed;if (filter === 'completed') return todo.completed;return true;});return (<div><div><button onClick={() => setFilter('all')}>All</button><button onClick={() => setFilter('active')}>Active</button><button onClick={() => setFilter('completed')}>Completed</button></div>{filteredTodos.map(todo => (<TodoItemkey={todo.id}todo={todo}onToggle={onToggle}onDelete={onDelete}/>))}</div>);
};export default TodoList;
// App.tsx
import React, { useState } from 'react';
import TodoList from './components/TodoList';
import { Todo } from './models/todo';const App: React.FC = () => {const [todos, setTodos] = useState<Todo[]>([]);const [input, setInput] = useState('');const addTodo = () => {if (!input.trim()) return;const newTodo: Todo = {id: Date.now(),text: input,completed: false};setTodos([...todos, newTodo]);setInput('');};const toggleTodo = (id: number) => {setTodos(todos.map(todo =>todo.id === id ? { ...todo, completed: !todo.completed } : todo));};const deleteTodo = (id: number) => {setTodos(todos.filter(todo => todo.id !== id));};return (<div><h1>Todo App</h1><div><inputtype="text"value={input}onChange={e => setInput(e.target.value)}onKeyPress={e => e.key === 'Enter' && addTodo()}/><button onClick={addTodo}>Add</button></div><TodoListtodos={todos}onToggle={toggleTodo}onDelete={deleteTodo}/></div>);
};export default App;