TypeScript完全指南:从JavaScript到TypeScript的进阶之路
·
一、TypeScript基础概念
1.1 什么是TypeScript?
TypeScript是JavaScript的超集,由微软开发,在JavaScript的基础上添加了静态类型系统。它可以在编译时捕获错误,提高代码的可维护性和开发效率。
1.2 TypeScript的历史
- 2012年:微软发布TypeScript 0.8版本
- 2013年:TypeScript 1.0发布,支持类型推断
- 2014年:TypeScript 1.1发布,性能大幅提升
- 2016年:TypeScript 2.0发布,引入不可空类型
- 2018年:TypeScript 3.0发布,支持项目引用
- 2020年:TypeScript 4.0发布,引入更多高级特性
- 至今:持续快速发展,版本迭代到5.x
1.3 TypeScript的优势
- 静态类型检查:在编译时发现错误,减少运行时错误
- 更好的IDE支持:自动补全、类型提示、重构功能
- 代码可读性:类型注解让代码更易理解
- 更好的维护性:大型项目更易于维护和重构
- 面向对象编程:完整的类、接口、抽象类支持
- 现代JavaScript特性:支持最新的ECMAScript特性
- 渐进式采用:可以逐步从JavaScript迁移
1.4 TypeScript与JavaScript的关系
// TypeScript是JavaScript的超集
// 所有有效的JavaScript代码都是有效的TypeScript代码
// JavaScript代码
function greet(name) {
return 'Hello, ' + name;
}
// TypeScript代码(完全兼容JavaScript)
function greet(name: string): string {
return 'Hello, ' + name;
}
二、TypeScript安装和配置
2.1 安装TypeScript
# 全局安装
npm install -g typescript
# 局部安装(推荐)
npm install --save-dev typescript
# 验证安装
tsc --version
2.2 创建第一个TypeScript项目
# 创建项目目录
mkdir my-typescript-app
cd my-typescript-app
# 初始化项目
npm init -y
# 安装TypeScript
npm install --save-dev typescript
# 创建TypeScript文件
touch hello.ts
// hello.ts
const message: string = 'Hello, TypeScript!';
console.log(message);
# 编译TypeScript文件
tsc hello.ts
# 运行编译后的JavaScript文件
node hello.js
2.3 tsconfig.json配置文件
{
"compilerOptions": {
/* 基本选项 */
"target": "ES2020", // 编译目标
"module": "commonjs", // 模块系统
"lib": ["ES2020", "DOM"], // 包含的库文件
"outDir": "./dist", // 输出目录
"rootDir": "./src", // 源代码目录
"removeComments": true, // 删除注释
"strict": true, // 启用所有严格类型检查选项
/* 额外检查 */
"noUnusedLocals": true, // 检查未使用的局部变量
"noUnusedParameters": true, // 检查未使用的参数
"noImplicitReturns": true, // 检查函数是否有返回值
"noFallthroughCasesInSwitch": true, // 防止switch语句穿透
/* 模块解析选项 */
"moduleResolution": "node", // 模块解析策略
"baseUrl": "./", // 基础路径
"paths": { // 路径映射
"@/*": ["src/*"]
},
"esModuleInterop": true, // 启用ES模块互操作
"allowSyntheticDefaultImports": true, // 允许合成默认导入
/* 高级选项 */
"skipLibCheck": true, // 跳过库文件的类型检查
"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
/* 源映射 */
"sourceMap": true, // 生成source map文件
"declaration": true, // 生成声明文件
"declarationMap": true // 生成声明文件source map
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
2.4 编译选项详解
# 监听模式
tsc --watch
# 只编译错误不生成文件
tsc --noEmit
# 删除生成的文件
tsc --clean
# 显示详细错误信息
tsc --pretty false
# 指定配置文件
tsc --project tsconfig.json
# 编译单个文件
tsc hello.ts
# 编译整个目录
tsc src
三、TypeScript基础类型
3.1 基本类型
// 布尔值
let isDone: boolean = false;
let isActive: boolean = true;
// 数字
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
// 字符串
let color: string = "blue";
let fullName: string = `Bob Smith`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;
// 数组
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
// 元组
let x: [string, number];
x = ["hello", 10]; // 正确
x = [10, "hello"]; // 错误
// 枚举
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green = 2, Blue = 4}
let colorName: string = Color[2]; // 显示'Green'
// Any(任意类型)
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
// Unknown(类型安全的any)
let userInput: unknown;
let userName: string;
userInput = 5;
userInput = "Max";
if (typeof userInput === "string") {
userName = userInput; // 类型安全
}
// 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);
}
// Object
let obj: object = { name: "张三", age: 25 };
3.2 类型注解
// 变量类型注解
let name: string = "张三";
let age: number = 25;
let isStudent: boolean = true;
// 函数类型注解
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 ? `${greeting}, ${name}` : `Hello, ${name}`;
}
// 默认参数
function greetWithDefault(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}`;
}
// 剩余参数
function sumAll(...numbers: number[]): number {
return numbers.reduce((acc, num) => acc + num, 0);
}
// 对象类型注解
let user: { name: string; age: number } = {
name: "张三",
age: 25
};
// 可选属性
let person: { name: string; age?: number } = {
name: "李四"
};
// 只读属性
let point: { readonly x: number; readonly y: number } = {
x: 10,
y: 20
};
// point.x = 5; // 错误:不能修改只读属性
// 函数类型
let myAdd: (x: number, y: number) => number = function(
x: number,
y: number
): number {
return x + y;
};
四、接口(Interface)
4.1 基本接口
// 定义接口
interface Person {
name: string;
age: number;
}
// 使用接口
let user: Person = {
name: "张三",
age: 25
};
// 函数参数使用接口
function greet(person: Person): void {
console.log(`Hello, ${person.name}!`);
}
4.2 可选属性
interface Car {
brand: string;
model: string;
year?: number; // 可选属性
}
let myCar: Car = {
brand: "Toyota",
model: "Camry"
};
4.3 只读属性
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // 错误:不能修改只读属性
// ReadonlyArray
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
// ro[0] = 12; // 错误
// ro.push(5); // 错误
// ro.length = 100; // 错误
// a = ro; // 错误
a = ro as number[]; // 可以通过类型断言绕过
4.4 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string): boolean {
let result = source.search(subString);
return result > -1;
};
4.5 可索引类型
// 字符串索引签名
interface StringArray {
[index: number]: string;
}
let myArray: StringArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
// 数字索引签名
interface StringDictionary {
[index: string]: string;
}
let myDict: StringDictionary = {
firstName: "John",
lastName: "Doe"
};
4.6 类类型接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) {}
}
4.7 接口继承
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = {
color: "red",
sideLength: 10
};
// 多重继承
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
4.8 混合类型接口
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = (function(start: number) {} as unknown) as Counter;
counter.interval = 123;
counter.reset = function() {};
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
五、类型别名和联合类型
5.1 类型别名
// 基本类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === "string") {
return n;
} else {
return n();
}
}
// 对象类型别名
type User = {
name: string;
age: number;
};
let user: User = {
name: "张三",
age: 25
};
// 函数类型别名
type GreetFunction = (name: string) => void;
const greet: GreetFunction = (name: string) => {
console.log(`Hello, ${name}!`);
};
5.2 联合类型
// 基本联合类型
let value: string | number;
value = "hello";
value = 123;
// 函数参数联合类型
function printId(id: number | string) {
console.log(`Your ID is: ${id}`);
}
// 类型守卫
function printIdWithGuard(id: number | string) {
if (typeof id === "string") {
console.log(id.toUpperCase()); // id是string
} else {
console.log(id.toFixed(2)); // id是number
}
}
5.3 交叉类型
interface BusinessPartner {
name: string;
credit: number;
}
interface Identity {
id: number;
name: string;
}
type Employee = BusinessPartner & Identity;
let emp: Employee = {
name: "张三",
credit: 1000,
id: 1
};
5.4 字面量类型
// 字符串字面量类型
type EventName = 'click' | 'dblclick' | 'mouseup' | 'mousedown';
function handleEvent(eventName: EventName) {
// 处理事件
}
handleEvent('click'); // 正确
// handleEvent('dblclicks'); // 错误
// 数字字面量类型
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
let diceRoll: Dice = 3;
// 布尔字面量类型
type Success = true;
type Failure = false;
type Result = Success | Failure;
六、类(Class)
6.1 基本类
class Person {
// 属性
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): void {
console.log(`Hello, my name is ${this.name}`);
}
// Getter
get info(): string {
return `${this.name} is ${this.age} years old`;
}
// Setter
set newName(name: string) {
this.name = name;
}
}
const person = new Person("张三", 25);
person.greet();
console.log(person.info);
person.newName = "李四";
6.2 继承
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
class Horse extends Animal {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);
6.3 访问修饰符
class Person {
public name: string; // 默认为public
private age: number; // 私有属性
protected address: string; // 受保护属性
readonly id: number; // 只读属性
constructor(name: string, age: number, address: string, id: number) {
this.name = name;
this.age = age;
this.address = address;
this.id = id;
}
public greet(): void {
console.log(`Hello, I'm ${this.name}`);
}
private getAge(): number {
return this.age;
}
protected getAddress(): string {
return this.address;
}
}
class Employee extends Person {
private salary: number;
constructor(
name: string,
age: number,
address: string,
id: number,
salary: number
) {
super(name, age, address, id);
this.salary = salary;
}
public getInfo(): string {
// 可以访问protected属性
return `${this.name}, ${this.address}, Salary: ${this.salary}`;
}
}
6.4 静态属性和方法
class MathHelper {
static PI: number = 3.14159;
static calculateCircleArea(radius: number): number {
return this.PI * radius * radius;
}
}
console.log(MathHelper.PI); // 3.14159
console.log(MathHelper.calculateCircleArea(5)); // 78.53975
6.5 抽象类
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("moving...");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof! Woof!");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("Meow!");
}
}
const dog = new Dog();
dog.makeSound(); // "Woof! Woof!"
dog.move(); // "moving..."
const cat = new Cat();
cat.makeSound(); // "Meow!"
cat.move(); // "moving..."
七、泛型(Generics)
7.1 基本泛型
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString"); // 类型推断为string
let output2 = identity<number>(100); // 类型推断为number
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
7.2 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {
return x + y;
};
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) {
return x + y;
};
7.3 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们可以访问length属性
return arg;
}
loggingIdentity({ length: 10, value: 3 }); // 正确
// loggingIdentity(3); // 错误:number没有length属性
7.4 泛型接口
interface Box<T> {
value: T;
}
let box: Box<string> = {
value: "hello"
};
let numberBox: Box<number> = {
value: 123
};
7.5 泛型类型别名
type Container<T> = { value: T };
type Tree<T> = {
value: T;
left: Tree<T> | null;
right: Tree<T> | null;
};
八、装饰器(Decorators)
8.1 类装饰器
// 类装饰器
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;
}
}
8.2 方法装饰器
// 方法装饰器
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${key} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`${key} returned:`, result);
return result;
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3);
8.3 属性装饰器
// 属性装饰器
function format(target: any, key: string) {
let value = target[key];
const getter = () => {
return value;
};
const setter = (newVal: string) => {
value = newVal.trim();
};
Object.defineProperty(target, key, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}
class Person {
@format
name: string;
constructor(name: string) {
this.name = name;
}
}
const person = new Person(" 张三 ");
console.log(person.name); // "张三"
8.4 参数装饰器
// 参数装饰器
function required(target: any, key: string, index: number) {
console.log(`Required parameter at index ${index} in method ${key}`);
}
class Greeter {
greet(@required name: string) {
return "Hello " + name;
}
}
九、模块系统
9.1 导出和导入
// 导出
// utils.ts
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export const PI = 3.14159;
export interface User {
name: string;
age: number;
}
export class Calculator {
add(a: number, b: number): number {
return a + b;
}
}
// 默认导出
export default function greet(name: string): string {
return `Hello, ${name}!`;
}
// 导入
// main.ts
import greet from './utils'; // 默认导入
import { add, subtract, PI } from './utils'; // 命名导入
import { add as addition } from './utils'; // 重命名导入
import * as utils from './utils'; // 导入所有
import Calculator from './utils'; // 导入类
// 使用
greet("张三");
add(1, 2);
utils.subtract(3, 1);
9.2 模块解析
// 相对路径导入
import { add } from './utils';
import { subtract } from '../math/utils';
// 绝对路径导入
import { greet } from 'lodash';
// 使用@types类型定义
import { User } from 'lodash';
十、高级类型
10.1 类型推断
// 变量类型推断
let x = 3; // 推断为number
let y = "hello"; // 推断为string
// 函数返回类型推断
function add(a: number, b: number) {
return a + b; // 推断返回类型为number
}
// 最佳通用类型推断
let zoo = [new Rhino(), new Elephant(), new Snake()];
// 推断为(Rhino | Elephant | Snake)[]
10.2 类型断言
// 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as语法(JSX中必须使用)
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
// 类型断言的使用场景
interface User {
name: string;
age: number;
}
let user: any = { name: "张三", age: 25 };
let typedUser = user as User;
10.3 类型守卫
// typeof类型守卫
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
// instanceof类型守卫
interface Padder {
getPaddingString(): string;
}
class SpaceRepeatingPadder implements Padder {
constructor(private spaces: number) {}
getPaddingString() {
return Array(this.spaces + 1).join(" ");
}
}
class StringPadder implements Padder {
constructor(private value: string) {}
getPaddingString() {
return this.value;
}
}
function getRandomPadder() {
return Math.random() < 0.5
? new SpaceRepeatingPadder(4)
: new StringPadder(" ");
}
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
padder; // 类型是SpaceRepeatingPadder
}
if (padder instanceof StringPadder) {
padder; // 类型是StringPadder
}
10.4 空值合并和可选链
// 空值合并运算符
const foo = null ?? 'default string';
console.log(foo); // 'default string'
const baz = 0 ?? 42;
console.log(baz); // 0
// 可选链
interface User {
name: string;
address?: {
city: string;
street: string;
};
}
const user: User = { name: "张三" };
const city = user?.address?.city; // undefined
const street = user?.address?.street; // undefined
10.5 条件类型
// 基本条件类型
type MessageOf<T> = T extends { message: unknown } ? T['message'] : never;
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
type EmailMessageContents = MessageOf<Email>;
// string
type DogMessageContents = MessageOf<Dog>;
// never
// 条件类型约束
type Flatten<T> = T extends any[] ? T[number] : T;
type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number
10.6 映射类型
// 基本映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;
// 高级映射类型
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface Person {
name: string;
age: number;
}
type LazyPerson = Getters<Person>;
// {
// getName: () => string;
// getAge: () => number;
// }
10.7 内置工具类型
// Partial<T> - 将所有属性变为可选
interface User {
name: string;
age: number;
}
type PartialUser = Partial<User>;
// Required<T> - 将所有属性变为必需
type RequiredUser = Required<PartialUser>;
// Readonly<T> - 将所有属性变为只读
type ReadonlyUser = Readonly<User>;
// Pick<T, K> - 选择特定属性
type NameAndAge = Pick<User, "name" | "age">;
// Omit<T, K> - 排除特定属性
type NameOnly = Omit<User, "age">;
// Record<K, T> - 创建对象类型
type UserRecord = Record<string, User>;
// Exclude<T, U> - 从类型中排除
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract<T, U> - 从类型中提取
type T2 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T> - 排除null和undefined
type T3 = NonNullable<string | null | undefined>; // string
// ReturnType<T> - 获取函数返回类型
type T4 = ReturnType<() => string>; // string
// Parameters<T> - 获取函数参数类型
type T5 = Parameters<(x: number, y: number) => void>; // [number, number]
十一、TypeScript与JavaScript的区别
11.1 主要区别对比
| 特性 | JavaScript | TypeScript |
|---|---|---|
| 类型系统 | 动态类型 | 静态类型 |
| 编译 | 直接执行 | 需要编译为JavaScript |
| 类型检查 | 运行时检查 | 编译时检查 |
| IDE支持 | 基础支持 | 完整支持(智能提示、重构) |
| 错误发现 | 运行时 | 编译时 |
| 面向对象 | 原型继承 | 类、接口、抽象类 |
| 代码可读性 | 需要注释 | 类型注解提高可读性 |
| 学习曲线 | 平缓 | 陡峭 |
| 文件扩展名 | .js | .ts |
| 泛型 | 不支持 | 支持 |
| 装饰器 | 不支持 | 支持 |
| 命名空间 | 不支持 | 支持 |
11.2 代码对比示例
JavaScript代码
// user.js
function createUser(name, age) {
return {
name: name,
age: age,
greet: function() {
console.log('Hello, ' + this.name);
}
};
}
const user = createUser('张三', 25);
user.greet();
// 可能的运行时错误
user.greet(); // 正确
user.age.toUpperCase(); // 运行时错误:user.age是数字,没有toUpperCase方法
TypeScript代码
// user.ts
interface User {
name: string;
age: number;
greet(): void;
}
function createUser(name: string, age: number): User {
return {
name: name,
age: age,
greet: function() {
console.log('Hello, ' + this.name);
}
};
}
const user: User = createUser('张三', 25);
user.greet();
// 编译时就能发现错误
// user.age.toUpperCase(); // 编译错误:类型'number'上不存在属性'toUpperCase'
11.3 函数对比
// JavaScript
function add(a, b) {
return a + b;
}
add(1, 2); // 3
add('1', '2'); // '12'
add([1], [2]); // '1,2'
// TypeScript
function add(a: number, b: number): number {
return a + b;
}
add(1, 2); // 3
// add('1', '2'); // 编译错误:类型'string'的参数不能赋给类型'number'的参数
// add([1], [2]); // 编译错误:类型'number[]'的参数不能赋给类型'number'的参数
11.4 对象对比
// JavaScript
const user = {
name: '张三',
age: 25
};
console.log(user.name); // '张三'
console.log(user.address); // undefined
// TypeScript
interface User {
name: string;
age: number;
address?: string; // 可选属性
}
const user: User = {
name: '张三',
age: 25
};
console.log(user.name); // '张三'
console.log(user.address); // undefined,但有类型检查
// const invalidUser: User = { name: '李四' };
// 编译错误:类型'{ name: string; }'中缺少属性'age'
11.5 数组对比
// JavaScript
const numbers = [1, 2, 3];
const mixed = [1, 'hello', true];
numbers.push(4);
mixed.push({ name: 'object' }); // 可以添加任何类型
// TypeScript
const numbers: number[] = [1, 2, 3];
const mixed: (number | string | boolean)[] = [1, 'hello', true];
numbers.push(4);
// numbers.push('5'); // 编译错误:类型'string'的参数不能赋给类型'number'的参数
// mixed.push({ name: 'object' }); // 编译错误:类型'{ name: string; }'不能赋给类型'number | string | boolean'
十二、TypeScript最佳实践
12.1 配置最佳实践
{
"compilerOptions": {
"strict": true, // 启用严格模式
"noImplicitAny": true, // 禁止隐式any
"strictNullChecks": true, // 严格null检查
"strictFunctionTypes": true, // 严格函数类型
"noUnusedLocals": true, // 检查未使用的局部变量
"noUnusedParameters": true, // 检查未使用的参数
"noImplicitReturns": true, // 检查函数返回值
"esModuleInterop": true, // ES模块互操作
"skipLibCheck": true // 跳过库文件检查
}
}
12.2 代码组织最佳实践
// 使用接口定义数据结构
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
// 使用类型别名定义联合类型
type Status = 'pending' | 'active' | 'completed';
// 使用枚举定义常量
enum Role {
ADMIN = 'admin',
USER = 'user',
GUEST = 'guest'
}
// 模块化导出
export { User, Status, Role };
// 使用命名空间组织相关功能
namespace Auth {
export function login(username: string, password: string): void {
// 登录逻辑
}
export function logout(): void {
// 登出逻辑
}
}
12.3 类型定义最佳实践
// 1. 优先使用interface定义对象类型
interface Person {
name: string;
age: number;
}
// 2. 使用type定义联合类型、交叉类型
type StringOrNumber = string | number;
// 3. 避免使用any,使用unknown代替
function processData(data: unknown) {
if (typeof data === 'string') {
console.log(data.toUpperCase());
}
}
// 4. 使用readonly定义不可变数据
interface Config {
readonly apiUrl: string;
readonly timeout: number;
}
// 5. 使用泛型提高代码复用性
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
12.4 错误处理最佳实践
// 使用类型守卫进行类型检查
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function processValue(value: unknown) {
if (isString(value)) {
console.log(value.toUpperCase());
} else {
console.log('Not a string');
}
}
// 使用错误类型
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
this.name = 'ValidationError';
}
}
function validateEmail(email: string): void {
if (!email.includes('@')) {
throw new ValidationError('email', 'Invalid email format');
}
}
// 使用Result类型处理可能失败的操作
type Result<T, E = Error> = {
success: true;
value: T;
} | {
success: false;
error: E;
};
function safeParseJSON(json: string): Result<object> {
try {
return {
success: true,
value: JSON.parse(json)
};
} catch (error) {
return {
success: false,
error: error as Error
};
}
}
十三、实战项目:TypeScript + Express
13.1 项目初始化
# 创建项目目录
mkdir ts-express-app
cd ts-express-app
# 初始化项目
npm init -y
# 安装依赖
npm install express
npm install --save-dev typescript @types/express @types/node ts-node nodemon
# 创建配置文件
touch tsconfig.json
13.2 tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
13.3 用户模型和类型定义
// src/types/user.types.ts
export interface User {
id: number;
name: string;
email: string;
age: number;
createdAt: Date;
updatedAt: Date;
}
export interface CreateUserDto {
name: string;
email: string;
age: number;
}
export interface UpdateUserDto {
name?: string;
email?: string;
age?: number;
}
export interface ErrorResponse {
message: string;
errors?: Record<string, string[]>;
}
13.4 验证中间件
// src/middleware/validation.middleware.ts
import { Request, Response, NextFunction } from 'express';
export const validate = (
schema: Record<string, any>
) => {
return (req: Request, res: Response, next: NextFunction) => {
const errors: Record<string, string[]> = {};
for (const [field, rules] of Object.entries(schema)) {
const value = req.body[field];
if (rules.required && (value === undefined || value === null)) {
if (!errors[field]) errors[field] = [];
errors[field].push(`${field} is required`);
continue;
}
if (rules.type && value !== undefined && typeof value !== rules.type) {
if (!errors[field]) errors[field] = [];
errors[field].push(`${field} must be a ${rules.type}`);
}
if (rules.min !== undefined && value !== undefined && value < rules.min) {
if (!errors[field]) errors[field] = [];
errors[field].push(`${field} must be at least ${rules.min}`);
}
if (rules.email && value !== undefined) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
if (!errors[field]) errors[field] = [];
errors[field].push(`${field} must be a valid email`);
}
}
}
if (Object.keys(errors).length > 0) {
return res.status(400).json({
message: 'Validation failed',
errors
});
}
next();
};
};
13.5 错误处理中间件
// src/middleware/error.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { ErrorResponse } from '../types/user.types';
export const errorHandler = (
error: Error,
req: Request,
res: Response<ErrorResponse>,
next: NextFunction
) => {
console.error('Error:', error);
res.status(500).json({
message: error.message || 'Internal server error'
});
};
export const notFoundHandler = (
req: Request,
res: Response<ErrorResponse>
) => {
res.status(404).json({
message: `Route ${req.method} ${req.url} not found`
});
};
13.6 用户控制器
// src/controllers/user.controller.ts
import { Request, Response } from 'express';
import { User, CreateUserDto, UpdateUserDto } from '../types/user.types';
// 模拟数据库
let users: User[] = [
{
id: 1,
name: '张三',
email: 'zhangsan@example.com',
age: 25,
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 2,
name: '李四',
email: 'lisi@example.com',
age: 30,
createdAt: new Date(),
updatedAt: new Date()
}
];
let nextId = 3;
export const getAllUsers = (req: Request, res: Response<User[]>) => {
res.json(users);
};
export const getUserById = (req: Request<{ id: string }>, res: Response<User | { message: string }>) => {
const id = parseInt(req.params.id);
const user = users.find(u => u.id === id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
};
export const createUser = (req: Request<{}, User, CreateUserDto>, res: Response<User>) => {
const newUser: User = {
id: nextId++,
...req.body,
createdAt: new Date(),
updatedAt: new Date()
};
users.push(newUser);
res.status(201).json(newUser);
};
export const updateUser = (
req: Request<{ id: string }, User, UpdateUserDto>,
res: Response<User | { message: string }>
) => {
const id = parseInt(req.params.id);
const index = users.findIndex(u => u.id === id);
if (index === -1) {
return res.status(404).json({ message: 'User not found' });
}
users[index] = {
...users[index],
...req.body,
updatedAt: new Date()
};
res.json(users[index]);
};
export const deleteUser = (req: Request<{ id: string }>, res: Response<{ message: string }>) => {
const id = parseInt(req.params.id);
const index = users.findIndex(u => u.id === id);
if (index === -1) {
return res.status(404).json({ message: 'User not found' });
}
users.splice(index, 1);
res.json({ message: 'User deleted successfully' });
};
13.7 用户路由
// src/routes/user.routes.ts
import { Router } from 'express';
import {
getAllUsers,
getUserById,
createUser,
updateUser,
deleteUser
} from '../controllers/user.controller';
import { validate } from '../middleware/validation.middleware';
const router = Router();
const userValidationSchema = {
name: { required: true, type: 'string' },
email: { required: true, type: 'string', email: true },
age: { required: true, type: 'number', min: 0 }
};
router.get('/', getAllUsers);
router.get('/:id', getUserById);
router.post('/', validate(userValidationSchema), createUser);
router.put('/:id', validate(userValidationSchema), updateUser);
router.delete('/:id', deleteUser);
export default router;
13.8 主应用文件
// src/app.ts
import express, { Application } from 'express';
import userRoutes from './routes/user.routes';
import { errorHandler, notFoundHandler } from './middleware/error.middleware';
const app: Application = express();
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.use('/api/users', userRoutes);
// 错误处理
app.use(notFoundHandler);
app.use(errorHandler);
export default app;
13.9 启动文件
// src/server.ts
import app from './app';
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
13.10 package.json脚本
{
"scripts": {
"dev": "nodemon --exec ts-node src/server.ts",
"build": "tsc",
"start": "node dist/server.js"
}
}
13.11 运行项目
# 开发模式
npm run dev
# 生产构建
npm run build
npm start
十四、总结
TypeScript是JavaScript的超集,通过添加静态类型系统,让代码更加安全、可维护。通过本文的学习,你应该掌握了:
- TypeScript的基础概念和优势
- TypeScript的安装和配置
- 基础类型和类型注解
- 接口和类型别名
- 类和继承
- 泛型的使用
- 装饰器
- 高级类型和工具类型
- TypeScript与JavaScript的详细对比
- TypeScript最佳实践
- 完整的TypeScript + Express实战项目
TypeScript vs JavaScript主要变化:
- 类型系统:从动态类型到静态类型,编译时就能发现错误
- 开发体验:更好的IDE支持,智能提示和重构
- 代码质量:类型注解让代码更易理解和维护
- 面向对象:完整的类、接口、抽象类支持
- 高级特性:泛型、装饰器、高级类型等
- 编译步骤:需要编译为JavaScript才能运行
- 学习曲线:比JavaScript更陡峭,但收益更大
学习建议:
- 从小项目开始,逐步迁移现有JavaScript项目
- 充分利用IDE的类型提示和自动补全
- 遵循严格的类型检查,避免使用any
- 学习高级类型,提高代码复用性
- 关注TypeScript的版本更新和新特性
- 参考开源项目的TypeScript使用方式
TypeScript的学习是一个持续的过程,随着对类型系统的深入理解,你将能编写出更加健壮、可维护的代码。保持学习的热情,不断提升自己的技能,你一定能成为一名优秀的TypeScript开发者!
希望这篇TypeScript详解教程对你有所帮助!如果你有任何问题或建议,欢迎留言讨论。持续学习,不断进步,让我们一起在TypeScript的世界里探索更多可能性!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)