TypeScript入门到精通
说实话,刚开始接触TypeScript的时候,我真的是又爱又恨。爱的是它的类型系统能让代码更加健壮,恨的是学习曲线太陡峭了,而且感觉写代码变慢了很多。但用了半年之后,我发现TypeScript真香!今天就和大家分享一下我在使用TypeScript过程中的一些心得和经验。
什么是TypeScript?
TypeScript是JavaScript的超集,由微软开发,它添加了静态类型系统和一些现代JavaScript的特性。简单来说,TypeScript = JavaScript + 类型系统 + ES6+特性。
为什么需要TypeScript?
- 类型安全:在编译时就能发现错误,而不是在运行时
- 更好的IDE支持:智能提示、代码导航、重构
- 代码可读性:通过类型定义让代码更清晰
- 重构信心:类型系统让重构更加安全
- 团队协作:统一的代码规范和接口定义
基础类型
基本类型
let message: string = 'Hello TypeScript';
let count: number = 42; let price: number = 19.99; let hex: number = 0xff; let binary: number = 0b1010;
let isActive: boolean = true;
let nullable: null = null; let undefinable: undefined = undefined;
let anyValue: any = '可以是任何类型'; let unknownValue: unknown = '类型未知';
function logMessage(message: string): void { console.log(message); }
function throwError(message: string): never { throw new Error(message); }
|
数组类型
let numbers: number[] = [1, 2, 3]; let strings: Array<string> = ['a', 'b', 'c'];
let tuple: [string, number] = ['hello', 123];
let readonlyArray: readonly number[] = [1, 2, 3];
const [first, second] = tuple;
|
对象类型
let user: { name: string; age: number; email?: string; } = { name: '张三', age: 25, email: 'zhangsan@example.com' };
interface User { name: string; age: number; email?: string; readonly id: string; sayHello(): void; }
let user2: User = { name: '李四', age: 30, id: '123', sayHello() { console.log('Hello'); } };
type Point = { x: number; y: number; };
let point: Point = { x: 10, y: 20 };
|
函数类型
function add(a: number, b: number): number { return a + b; }
const multiply = (a: number, b: number): number => { return a * b; };
function greet(name: string, greeting?: string): string { return `${greeting || 'Hello'}, ${name}!`; }
function createConfig(options: { port: number; host?: string; debug?: boolean; } = { port: 3000, debug: false }) { return options; }
function sum(...numbers: number[]): number { return numbers.reduce((acc, num) => acc + num, 0); }
function process(input: string): string; function process(input: number): number; function process(input: string | number): string | number { if (typeof input === 'string') { return input.toUpperCase(); } return input * 2; }
|
高级类型
联合类型和交叉类型
type Result = 'success' | 'error' | 'pending';
function handleResult(status: Result) { switch (status) { case 'success': console.log('操作成功'); break; case 'error': console.log('操作失败'); break; case 'pending': console.log('处理中...'); break; } }
type UserWithPermissions = User & { permissions: string[]; };
let admin: UserWithPermissions = { name: '管理员', age: 35, id: 'admin123', sayHello() { console.log('Hello admin'); }, permissions: ['read', 'write', 'delete'] };
|
类型推断
let inferredString = 'hello'; let inferredNumber = 42;
let someValue: any = 'this is a string'; let strLength: number = (someValue as string).length;
let potentiallyNull: string | null = Math.random() > 0.5 ? 'hello' : null; let length = potentiallyNull!.length;
|
泛型
function identity<T>(arg: T): T { return arg; }
let output1 = identity<string>('hello'); let output2 = identity(42);
function getFirstElement<T>(arr: T[]): T | undefined { return arr[0]; }
let numbersArray = [1, 2, 3]; let firstNumber = getFirstElement(numbersArray);
interface Lengthwise { length: number; }
function logLength<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; }
logLength('hello'); logLength([1, 2, 3]);
function createArray<T = number>(length: number, value: T): T[] { return Array(length).fill(value); }
let numberArray = createArray(3, 0); let stringArray = createArray(3, '');
|
类型守卫
function isString(value: any): value is string { return typeof value === 'string'; }
function processValue(value: string | number) { if (isString(value)) { console.log(value.toUpperCase()); } else { console.log(value.toFixed(2)); } }
class Dog { bark() { console.log('Woof!'); } }
class Cat { meow() { console.log('Meow!'); } }
function isAnimal(animal: Dog | Cat): animal is Dog { return (animal as Dog).bark !== undefined; }
function makeSound(animal: Dog | Cat) { if (isAnimal(animal)) { animal.bark(); } else { (animal as Cat).meow(); } }
interface Bird { fly(): void; }
interface Fish { swim(): void; }
function move(pet: Bird | Fish) { if ('fly' in pet) { pet.fly(); } else { pet.swim(); } }
|
实用工具类型
Partial 和 Required
interface User { name: string; age: number; email?: string; }
type PartialUser = Partial<User>; let partialUser: PartialUser = { name: '张三' };
type RequiredUser = Required<User>; let requiredUser: RequiredUser = { name: '李四', age: 30, email: 'lisi@example.com' };
|
Pick 和 Omit
interface Product { id: string; name: string; price: number; description: string; category: string; }
type ProductPreview = Pick<Product, 'id' | 'name' | 'price'>; let preview: ProductPreview = { id: '123', name: '产品名称', price: 99.99 };
type ProductDetails = Omit<Product, 'id' | 'category'>; let details: ProductDetails = { name: '产品名称', price: 99.99, description: '产品描述' };
|
Record 和 Map
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>; let roleMapping: UserRoles = { 'zhangsan': 'admin', 'lisi': 'user', 'guest': 'guest' };
const userMap = new Map<string, User>(); userMap.set('zhangsan', { name: '张三', age: 25 });
|
条件类型和映射类型
type ExtractType<T, U> = T extends U ? T : never; type Numbers = ExtractType<string | number | boolean, number>;
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
interface MutableUser { name: string; age: number; }
interface ReadonlyUser extends Readonly<MutableUser> {}
let readonlyUser: ReadonlyUser = { name: '张三', age: 25 };
type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; };
|
实战应用
React + TypeScript
import React from 'react';
interface ButtonProps { children: React.ReactNode; variant?: 'primary' | 'secondary' | 'danger'; size?: 'sm' | 'md' | 'lg'; onClick?: () => void; disabled?: boolean; }
const Button: React.FC<ButtonProps> = ({ children, variant = 'primary', size = 'md', onClick, disabled = false }) => { const className = `btn btn-${variant} btn-${size} ${disabled ? 'disabled' : ''}`; return ( <button className={className} onClick={onClick} disabled={disabled}> {children} </button> ); };
const App: React.FC = () => { const handleClick = () => { console.log('Button clicked'); }; return ( <div> <Button variant="primary" size="md" onClick={handleClick}> Click Me </Button> <Button variant="danger" size="sm"> Delete </Button> </div> ); };
export default App;
|
异步操作
interface ApiResponse<T> { data: T; status: number; message: string; }
async function fetchData<T>(url: string): Promise<ApiResponse<T>> { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }
interface User { id: string; name: string; email: string; }
async function loadUser(userId: string) { try { const response = await fetchData<User>(`/api/users/${userId}`); console.log(response.data); } catch (error) { console.error('Error loading user:', error); } }
|
数据库操作
interface User { id: string; name: string; email: string; age?: number; createdAt: Date; updatedAt: Date; }
type UserCreate = Omit<User, 'id' | 'createdAt' | 'updatedAt'>; type UserUpdate = Partial<UserCreate> & { id: string };
class UserService { async createUser(userData: UserCreate): Promise<User> { const user = { ...userData, id: generateId(), createdAt: new Date(), updatedAt: new Date() }; await db.collection('users').insertOne(user); return user; } async updateUser(userId: string, updateData: UserUpdate): Promise<User> { const result = await db.collection('users').findOneAndUpdate( { id: userId }, { $set: { ...updateData, updatedAt: new Date() } }, { returnDocument: 'after' } ); if (!result.value) { throw new Error('User not found'); } return result.value; } }
|
配置和工程化
tsconfig.json 配置
{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "lib": ["ES2020", "DOM", "DOM.Iterable"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", "strictNullChecks": true, "noImplicitAny": true, "strictBindCallApply": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "experimentalDecorators": true, "emitDecoratorMetadata": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "dist" ] }
|
ESLint 集成
{ "extends": [ "eslint:recommended", "@typescript-eslint/recommended" ], "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-explicit-any": "warn", "prefer-const": "error" } }
|
Prettier 配置
{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 100, "arrowParens": "avoid" }
|
性能优化技巧
1. 类型定义优化
interface User { name: string; age: number; }
function processUser(user: User): void { console.log(user.name, user.age); }
function processUserAny(user: any): void { }
function parseJSON<T>(json: string): T { return JSON.parse(json) as T; }
const user = parseJSON<User>('{"name": "张三", "age": 25}');
|
2. 懒加载类型
interface ComponentProps { }
const LazyComponent = React.lazy(() => import('./Component'));
<React.Suspense fallback={<div>Loading...</div>}> <LazyComponent {...props} /> </React.Suspense>
|
3. 类型复用
type PaginationParams = { page: number; limit: number; };
type FilterParams = { search?: string; category?: string; };
type ApiResponse<T> = { data: T[]; total: number; page: number; limit: number; };
function fetchPaginatedData<T>( url: string, params: PaginationParams & FilterParams ): Promise<ApiResponse<T>> { return fetch(url + '?' + new URLSearchParams(params as any)) .then(response => response.json()); }
|
常见问题和解决方案
1. 类型错误处理
interface User { name: string; email?: string; }
function getUserEmail(user: User): string { return user.email || 'default@example.com'; if (user.email) { return user.email; } throw new Error('User has no email'); }
function formatValue(value: string | number): string { if (typeof value === 'string') { return value.toUpperCase(); } return value.toString(); }
|
2. 泛型约束
interface ApiResponse<T> { data: T; status: number; }
function fetchData<T>(url: string): Promise<ApiResponse<T>> { return fetch(url).then(res => res.json()); }
function fetchDataBad(url: string, data: any): Promise<any> { return fetch(url, { body: JSON.stringify(data) }).then(res => res.json()); }
|
3. 类型推断优化
const users: Array<{ id: string; name: string }> = [ { id: '1', name: '张三' }, { id: '2', name: '李四' } ];
|
总结
TypeScript确实是一个强大的工具,虽然学习曲线有点陡峭,但一旦掌握了,它带来的好处是显而易见的。
在我的开发经历中,TypeScript让我受益匪浅:
- 代码质量提升:编译时就能发现很多错误
- 开发效率提高:智能提示和类型检查让开发更高效
- 重构更安全:有了类型系统,重构更有信心
- 代码可读性增强:类型定义让代码更清晰
- 团队协作更顺畅:统一的接口和类型定义
最后给大家一个小建议:不要一次性掌握所有TypeScript特性,从最常用的基础类型开始,逐步学习高级特性。在实际项目中多使用,遇到问题多查阅官方文档。
记住,TypeScript不是万能的,它只是一个工具。关键是要学会正确地使用它,让它为我们的开发工作带来便利。希望这篇文章能对你有所帮助,让我们一起在TypeScript的世界里探索更精彩的未来!