简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE português คนไทย Français

站内搜索

搜索

活动公告

11-02 12:46
10-23 09:32
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,将及时处理!
10-23 09:31
10-23 09:28
通知:签到时间调整为每日4:00(东八区)
10-23 09:26

TypeScript入门指南从基础语法到高级特性的完整学习路径

3万

主题

423

科技点

3万

积分

大区版主

木柜子打湿

积分
31916

三倍冰淇淋无人之境【一阶】财Doro小樱(小丑装)立华奏以外的星空【二阶】⑨的冰沙

发表于 2025-9-29 23:20:01 | 显示全部楼层 |阅读模式 [标记阅至此楼]

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. TypeScript简介

1.1 什么是TypeScript

TypeScript是由微软开发的一种开源编程语言,它是JavaScript的超集,添加了静态类型系统。TypeScript在编译时会被转换为纯JavaScript代码,这意味着任何现有的JavaScript代码都可以在TypeScript中运行,同时TypeScript还提供了额外的功能,如类型检查、接口、枚举等。

1.2 TypeScript与JavaScript的关系

TypeScript是JavaScript的超集,这意味着所有有效的JavaScript代码都是有效的TypeScript代码。TypeScript扩展了JavaScript,添加了类型系统和其他功能,这些功能在编译时会被移除,最终生成标准的JavaScript代码。

1.3 TypeScript的优势

1. 静态类型检查:在编译时捕获错误,而不是在运行时
2. 更好的IDE支持:提供代码补全、导航和重构功能
3. 增强的可读性和可维护性:类型注解使代码更易于理解
4. 先进的JavaScript特性:支持最新的ECMAScript特性
5. 大型项目的理想选择:通过类型系统帮助管理复杂项目

1.4 开发环境搭建

要开始使用TypeScript,你需要安装Node.js和TypeScript编译器:
  1. # 安装Node.js (如果尚未安装)
  2. # 访问 https://nodejs.org/ 下载并安装
  3. # 安装TypeScript编译器
  4. npm install -g typescript
  5. # 验证安装
  6. tsc --version
复制代码

创建你的第一个TypeScript文件:
  1. // hello.ts
  2. function greet(name: string): string {
  3.   return `Hello, ${name}!`;
  4. }
  5. const message = greet("TypeScript");
  6. console.log(message);
复制代码

编译并运行:
  1. tsc hello.ts
  2. node hello.js
复制代码

2. TypeScript基础语法

2.1 基本类型

TypeScript提供了多种基本类型,用于定义变量的类型:
  1. // 布尔类型
  2. let isDone: boolean = false;
  3. // 数字类型
  4. let decimal: number = 6;
  5. let hex: number = 0xf00d;
  6. let binary: number = 0b1010;
  7. let octal: number = 0o744;
  8. // 字符串类型
  9. let color: string = "blue";
  10. color = 'red';
  11. // 数组类型
  12. let list: number[] = [1, 2, 3];
  13. // 或者使用泛型
  14. let list2: Array<number> = [1, 2, 3];
  15. // 元组类型 - 允许表示一个已知元素数量和类型的数组
  16. let x: [string, number];
  17. x = ["hello", 10]; // 正确
  18. // x = [10, "hello"]; // 错误
  19. // 枚举类型
  20. enum Color {Red, Green, Blue}
  21. let c: Color = Color.Green;
  22. // Any类型 - 不希望类型检查器对这些值进行检查
  23. let notSure: any = 4;
  24. notSure = "maybe a string";
  25. notSure = false;
  26. // Void类型 - 表示没有任何类型
  27. function warnUser(): void {
  28.   console.log("This is a warning message");
  29. }
  30. // Null 和 Undefined
  31. let u: undefined = undefined;
  32. let n: null = null;
  33. // Never类型 - 表示永不存在的值的类型
  34. function error(message: string): never {
  35.   throw new Error(message);
  36. }
  37. // Object类型
  38. let obj: object = {name: "TypeScript"};
复制代码

2.2 变量声明

TypeScript支持JavaScript的变量声明方式,并添加了类型注解:
  1. // 使用var声明(不推荐,有作用域问题)
  2. var myVar: string = "Hello";
  3. // 使用let声明(推荐,块级作用域)
  4. let myLet: string = "Hello";
  5. // 使用const声明(推荐,常量)
  6. const myConst: string = "Hello";
  7. // 解构赋值
  8. let input = [1, 2];
  9. let [first, second] = input;
  10. // 对象解构
  11. let o = {
  12.     a: "foo",
  13.     b: 12,
  14.     c: "bar"
  15. };
  16. let {a, b} = o;
  17. // 类型断言
  18. let someValue: any = "this is a string";
  19. let strLength: number = (someValue as string).length;
  20. // 或者使用尖括号语法
  21. let strLength2: number = (<string>someValue).length;
复制代码

2.3 函数

TypeScript为函数添加了类型注解,使函数更加安全:
  1. // 基本函数类型
  2. function add(x: number, y: number): number {
  3.     return x + y;
  4. }
  5. // 完整函数类型
  6. let myAdd: (x: number, y: number) => number = function(x: number, y: number): number {
  7.     return x + y;
  8. };
  9. // 可选参数
  10. function buildName(firstName: string, lastName?: string): string {
  11.     if (lastName) {
  12.         return firstName + " " + lastName;
  13.     } else {
  14.         return firstName;
  15.     }
  16. }
  17. // 默认参数
  18. function buildName2(firstName: string, lastName: string = "Smith"): string {
  19.     return firstName + " " + lastName;
  20. }
  21. // 剩余参数
  22. function buildName3(firstName: string, ...restOfName: string[]): string {
  23.     return firstName + " " + restOfName.join(" ");
  24. }
  25. // 函数重载
  26. function pickCard(x: {suit: string; card: number;}[]): number;
  27. function pickCard(x: number): {suit: string; card: number;};
  28. function pickCard(x): any {
  29.     if (typeof x == "object") {
  30.         let pickedCard = Math.floor(Math.random() * x.length);
  31.         return pickedCard;
  32.     } else if (typeof x == "number") {
  33.         let pickedSuit = Math.floor(x / 13);
  34.         return { suit: suits[pickedSuit], card: x % 13 };
  35.     }
  36. }
复制代码

2.4 接口

接口是TypeScript的核心概念之一,用于定义对象的结构:
  1. // 基本接口
  2. interface Person {
  3.     name: string;
  4.     age: number;
  5. }
  6. function greet(person: Person): string {
  7.     return "Hello, " + person.name;
  8. }
  9. // 可选属性
  10. interface SquareConfig {
  11.     color?: string;
  12.     width?: number;
  13. }
  14. // 只读属性
  15. interface Point {
  16.     readonly x: number;
  17.     readonly y: number;
  18. }
  19. // 函数类型
  20. interface SearchFunc {
  21.     (source: string, subString: string): boolean;
  22. }
  23. // 可索引类型
  24. interface StringArray {
  25.     [index: number]: string;
  26. }
  27. // 类类型
  28. interface ClockInterface {
  29.     currentTime: Date;
  30.     setTime(d: Date): void;
  31. }
  32. class Clock implements ClockInterface {
  33.     currentTime: Date = new Date();
  34.     setTime(d: Date) {
  35.         this.currentTime = d;
  36.     }
  37.     constructor(h: number, m: number) { }
  38. }
  39. // 扩展接口
  40. interface Shape {
  41.     color: string;
  42. }
  43. interface Square extends Shape {
  44.     sideLength: number;
  45. }
复制代码

2.5 类

TypeScript中的类是对ES6类的扩展,添加了类型和其他特性:
  1. // 基本类
  2. class Greeter {
  3.     greeting: string;
  4.     constructor(message: string) {
  5.         this.greeting = message;
  6.     }
  7.     greet(): string {
  8.         return "Hello, " + this.greeting;
  9.     }
  10. }
  11. // 继承
  12. class Animal {
  13.     name: string;
  14.     constructor(theName: string) { this.name = theName; }
  15.     move(distanceInMeters: number = 0) {
  16.         console.log(`${this.name} moved ${distanceInMeters}m.`);
  17.     }
  18. }
  19. class Snake extends Animal {
  20.     constructor(name: string) { super(name); }
  21.     move(distanceInMeters = 5) {
  22.         console.log("Slithering...");
  23.         super.move(distanceInMeters);
  24.     }
  25. }
  26. // 公共、私有与受保护的修饰符
  27. class Animal2 {
  28.     public name: string;
  29.     private privateName: string;
  30.     protected protectedName: string;
  31.     constructor(theName: string) {
  32.         this.name = theName;
  33.         this.privateName = theName;
  34.         this.protectedName = theName;
  35.     }
  36. }
  37. // 只读属性
  38. class Octopus {
  39.     readonly name: string;
  40.     readonly numberOfLegs: number = 8;
  41.     constructor(theName: string) {
  42.         this.name = theName;
  43.     }
  44. }
  45. // 存取器
  46. let passcode = "secret passcode";
  47. class Employee {
  48.     private _fullName: string;
  49.     get fullName(): string {
  50.         return this._fullName;
  51.     }
  52.     set fullName(newName: string) {
  53.         if (passcode && passcode === "secret passcode") {
  54.             this._fullName = newName;
  55.         } else {
  56.             console.log("Error: Unauthorized update of employee!");
  57.         }
  58.     }
  59. }
  60. // 静态属性
  61. class Grid {
  62.     static origin = {x: 0, y: 0};
  63.     calculateDistanceFromOrigin(point: {x: number; y: number;}) {
  64.         let xDist = (point.x - Grid.origin.x);
  65.         let yDist = (point.y - Grid.origin.y);
  66.         return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
  67.     }
  68.     constructor(public scale: number) { }
  69. }
  70. // 抽象类
  71. abstract class Animal3 {
  72.     abstract makeSound(): void;
  73.     move(): void {
  74.         console.log("roaming the earth...");
  75.     }
  76. }
  77. class Dog extends Animal3 {
  78.     makeSound(): void {
  79.         console.log("Woof! Woof!");
  80.     }
  81. }
复制代码

2.6 泛型基础

泛型允许创建可重用的组件,一个组件可以支持多种类型的数据:
  1. // 基本泛型
  2. function identity<T>(arg: T): T {
  3.     return arg;
  4. }
  5. // 使用泛型
  6. let output = identity<string>("myString");
  7. let output2 = identity("myString"); // 类型推断
  8. // 泛型接口
  9. interface GenericIdentityFn {
  10.     <T>(arg: T): T;
  11. }
  12. // 泛型类
  13. class GenericNumber<T> {
  14.     zeroValue: T;
  15.     add: (x: T, y: T) => T;
  16. }
  17. let myGenericNumber = new GenericNumber<number>();
  18. myGenericNumber.zeroValue = 0;
  19. myGenericNumber.add = function(x, y) { return x + y; };
  20. // 泛型约束
  21. interface Lengthwise {
  22.     length: number;
  23. }
  24. function loggingIdentity<T extends Lengthwise>(arg: T): T {
  25.     console.log(arg.length);  // 现在我们知道arg具有length属性
  26.     return arg;
  27. }
复制代码

3. 中级特性

3.1 高级类型

TypeScript提供了多种高级类型,使类型系统更加灵活和强大:
  1. // 交叉类型
  2. interface BusinessPartner {
  3.     name: string;
  4.     credit: number;
  5. }
  6. interface Identity {
  7.     id: number;
  8.     name: string;
  9. }
  10. type Employee = BusinessPartner & Identity;
  11. // 联合类型
  12. function padLeft(value: string, padding: string | number) {
  13.     if (typeof padding === "number") {
  14.         return Array(padding + 1).join(" ") + value;
  15.     }
  16.     if (typeof padding === "string") {
  17.         return padding + value;
  18.     }
  19.     throw new Error(`Expected string or number, got '${padding}'.`);
  20. }
  21. // 类型别名
  22. type Name = string;
  23. type NameResolver = () => string;
  24. type NameOrResolver = Name | NameResolver;
  25. function getName(n: NameOrResolver): Name {
  26.     if (typeof n === "string") {
  27.         return n;
  28.     } else {
  29.         return n();
  30.     }
  31. }
  32. // 字面量类型
  33. type Easing = "ease-in" | "ease-out" | "ease-in-out";
  34. // 可辨识联合
  35. interface Square {
  36.     kind: "square";
  37.     size: number;
  38. }
  39. interface Rectangle {
  40.     kind: "rectangle";
  41.     width: number;
  42.     height: number;
  43. }
  44. interface Circle {
  45.     kind: "circle";
  46.     radius: number;
  47. }
  48. type Shape = Square | Rectangle | Circle;
  49. function area(s: Shape): number {
  50.     switch (s.kind) {
  51.         case "square": return s.size * s.size;
  52.         case "rectangle": return s.width * s.height;
  53.         case "circle": return Math.PI * s.radius ** 2;
  54.     }
  55. }
  56. // 索引类型
  57. function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  58.     return names.map(n => o[n]);
  59. }
  60. interface Person {
  61.     name: string;
  62.     age: number;
  63. }
  64. let person: Person = {
  65.     name: 'Jarid',
  66.     age: 35
  67. };
  68. let strings: string[] = pluck(person, ['name']); // ok, string[]
复制代码

3.2 类型守卫

类型守卫允许你在特定的代码块中缩小类型的范围:
  1. // typeof 类型守卫
  2. function padLeft(value: string, padding: string | number) {
  3.     if (typeof padding === "number") {
  4.         return Array(padding + 1).join(" ") + value;
  5.     }
  6.     if (typeof padding === "string") {
  7.         return padding + value;
  8.     }
  9.     throw new Error(`Expected string or number, got '${padding}'.`);
  10. }
  11. // instanceof 类型守卫
  12. class Bird {
  13.     fly() {
  14.         console.log("bird flies");
  15.     }
  16. }
  17. class Fish {
  18.     swim() {
  19.         console.log("fish swims");
  20.     }
  21. }
  22. function pet(pet: Bird | Fish) {
  23.     if (pet instanceof Bird) {
  24.         pet.fly();
  25.     } else if (pet instanceof Fish) {
  26.         pet.swim();
  27.     }
  28. }
  29. // 自定义类型守卫
  30. function isFish(pet: Fish | Bird): pet is Fish {
  31.     return (<Fish>pet).swim !== undefined;
  32. }
  33. // 可null类型
  34. function f(sn: string | null): string {
  35.     if (sn === null) {
  36.         return "default";
  37.     } else {
  38.         return sn;
  39.     }
  40. }
复制代码

3.3 装饰器

装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问符、属性或参数上:
  1. // 类装饰器
  2. function sealed(constructor: Function) {
  3.     Object.seal(constructor);
  4.     Object.seal(constructor.prototype);
  5. }
  6. @sealed
  7. class Greeter {
  8.     greeting: string;
  9.     constructor(message: string) {
  10.         this.greeting = message;
  11.     }
  12.     greet() {
  13.         return "Hello, " + this.greeting;
  14.     }
  15. }
  16. // 方法装饰器
  17. function enumerable(value: boolean) {
  18.     return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  19.         descriptor.enumerable = value;
  20.     };
  21. }
  22. class Greeter2 {
  23.     greeting: string;
  24.     constructor(message: string) {
  25.         this.greeting = message;
  26.     }
  27.     @enumerable(false)
  28.     greet() {
  29.         return "Hello, " + this.greeting;
  30.     }
  31. }
  32. // 访问器装饰器
  33. class Point {
  34.     private _x: number;
  35.     private _y: number;
  36.     constructor(x: number, y: number) {
  37.         this._x = x;
  38.         this._y = y;
  39.     }
  40.     @configurable(false)
  41.     get x() { return this._x; }
  42.     @configurable(false)
  43.     get y() { return this._y; }
  44. }
  45. function configurable(value: boolean) {
  46.     return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  47.         descriptor.configurable = value;
  48.     };
  49. }
  50. // 属性装饰器
  51. class Greeter3 {
  52.     @format("Hello, %s")
  53.     greeting: string;
  54.     constructor(message: string) {
  55.         this.greeting = message;
  56.     }
  57.     greet() {
  58.         let greetingString = this.greeting;
  59.         return greetingString;
  60.     }
  61. }
  62. import "reflect-metadata";
  63. function format(formatString: string) {
  64.     return function (target: any, propertyKey: string): void {
  65.         Reflect.defineMetadata("format", formatString, target, propertyKey);
  66.     };
  67. }
  68. // 参数装饰器
  69. class Greeter4 {
  70.     greeting: string;
  71.     constructor(message: string) {
  72.         this.greeting = message;
  73.     }
  74.     @validate
  75.     greet(@required name: string) {
  76.         return "Hello " + name + ", " + this.greeting;
  77.     }
  78. }
  79. import "reflect-metadata";
  80. function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  81.     let requiredParams: number[] = Reflect.getOwnMetadata("required", target, propertyKey) || [];
  82.     requiredParams.push(parameterIndex);
  83.     Reflect.defineMetadata("required", requiredParams, target, propertyKey);
  84. }
  85. function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<Function>) {
  86.     let method = descriptor.value;
  87.    
  88.     descriptor.value = function () {
  89.         let requiredParams: number[] = Reflect.getOwnMetadata("required", target, propertyName);
  90.         if (requiredParams) {
  91.             for (let parameterIndex of requiredParams) {
  92.                 if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) {
  93.                     throw new Error("Missing required argument.");
  94.                 }
  95.             }
  96.         }
  97.         
  98.         return method.apply(this, arguments);
  99.     }
  100. }
复制代码

3.4 模块系统

TypeScript支持ES模块和CommonJS模块系统:
  1. // 导出
  2. // 导出声明
  3. export interface StringValidator {
  4.     isAcceptable(s: string): boolean;
  5. }
  6. // 导出语句
  7. export class ZipCodeValidator implements StringValidator {
  8.     isAcceptable(s: string) {
  9.         return s.length === 5 && numberRegexp.test(s);
  10.     }
  11. }
  12. // 导入
  13. import { StringValidator } from "./StringValidator";
  14. import { ZipCodeValidator } from "./ZipCodeValidator";
  15. // 默认导出
  16. export default class ZipCodeValidator {
  17.     // ...
  18. }
  19. // 导入默认导出
  20. import ZipCodeValidator from "./ZipCodeValidator";
  21. // 导出所有
  22. export * from "./StringValidator";
  23. // 重新导出
  24. export { ZipCodeValidator as RegExpBasedZipCodeValidator } from "./ZipCodeValidator";
复制代码

3.5 命名空间

命名空间是TypeScript中组织代码的一种方式,特别适用于避免全局命名冲突:
  1. // 基本命名空间
  2. namespace Validation {
  3.     export interface StringValidator {
  4.         isAcceptable(s: string): boolean;
  5.     }
  6.     const lettersRegexp = /^[A-Za-z]+$/;
  7.     const numberRegexp = /^[0-9]+$/;
  8.     export class LettersOnlyValidator implements StringValidator {
  9.         isAcceptable(s: string) {
  10.             return lettersRegexp.test(s);
  11.         }
  12.     }
  13.     export class ZipCodeValidator implements StringValidator {
  14.         isAcceptable(s: string) {
  15.             return s.length === 5 && numberRegexp.test(s);
  16.         }
  17.     }
  18. }
  19. // 使用命名空间
  20. let validators: { [s: string]: Validation.StringValidator; } = {};
  21. validators["ZIP code"] = new Validation.ZipCodeValidator();
  22. // 嵌套命名空间
  23. namespace Shapes {
  24.     namespace Polygons {
  25.         export class Triangle { }
  26.         export class Square { }
  27.     }
  28. }
  29. // 使用嵌套命名空间
  30. let sq = new Shapes.Polygons.Square();
  31. // 命名空间别名
  32. import polygons = Shapes.Polygons;
  33. let tri = new polygons.Triangle();
复制代码

4. 高级特性

4.1 高级泛型

TypeScript的泛型系统非常强大,支持复杂的类型操作:
  1. // 泛型约束与索引类型
  2. function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  3.     return obj[key];
  4. }
  5. let x = { a: 1, b: 2, c: 3, d: 4 };
  6. getProperty(x, "a"); // okay
  7. getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
  8. // 泛型与new
  9. function create<T>(c: { new(): T; }): T {
  10.     return new c();
  11. }
  12. // 泛型与默认类型
  13. interface GenericIdentityFn<T = string> {
  14.     (arg: T): T;
  15. }
  16. // 泛型与条件类型
  17. type TypeName<T> =
  18.     T extends string ? "string" :
  19.     T extends number ? "number" :
  20.     T extends boolean ? "boolean" :
  21.     T extends undefined ? "undefined" :
  22.     T extends Function ? "function" :
  23.     "object";
  24. type T0 = TypeName<string>;  // "string"
  25. type T1 = TypeName<"a">;  // "string"
  26. type T2 = TypeName<true>;  // "boolean"
  27. type T3 = TypeName<() => void>;  // "function"
  28. type T4 = TypeName<string[]>;  // "object"
  29. // 泛型与映射类型
  30. type Readonly<T> = {
  31.     readonly [P in keyof T]: T[P];
  32. };
  33. type Partial<T> = {
  34.     [P in keyof T]?: T[P];
  35. };
  36. type Person = {
  37.     name: string;
  38.     age: number;
  39. };
  40. type ReadonlyPerson = Readonly<Person>;
  41. type PartialPerson = Partial<Person>;
复制代码

4.2 条件类型

条件类型允许根据条件选择类型:
  1. // 基本条件类型
  2. type NonNullable<T> = T extends null | undefined ? never : T;
  3. // 分布式条件类型
  4. type ToArray<T> = T extends any ? T[] : never;
  5. type StrOrNumArray = ToArray<string | number>;  // string[] | number[]
  6. // 条件类型与推断
  7. type ExtractType<T> = T extends Promise<infer U> ? U : never;
  8. type PromiseType = ExtractType<Promise<string>>;  // string
  9. // 条件类型与映射
  10. type ExtractInfo<T> = {
  11.     [K in keyof T]: T[K] extends Function ? K : never;
  12. }[keyof T];
  13. interface User {
  14.     id: number;
  15.     name: string;
  16.     updateName: (newName: string) => void;
  17. }
  18. type FunctionKeys = ExtractInfo<User>;  // "updateName"
复制代码

4.3 映射类型

映射类型允许基于现有类型创建新类型:
  1. // 基本映射类型
  2. type OptionsFlags<T> = {
  3.     [K in keyof T]: boolean;
  4. };
  5. interface FeatureFlags {
  6.     darkMode: () => void;
  7.     newUserProfile: () => void;
  8. }
  9. type FeatureOptions = OptionsFlags<FeatureFlags>;
  10. // 等同于:
  11. // {
  12. //     darkMode: boolean;
  13. //     newUserProfile: boolean;
  14. // }
  15. // 修改属性
  16. type CreateMutable<T> = {
  17.     -readonly [P in keyof T]: T[P];
  18. };
  19. type LockedAccount = {
  20.     readonly id: string;
  21.     readonly name: string;
  22. };
  23. type UnlockedAccount = CreateMutable<LockedAccount>;
  24. // 等同于:
  25. // {
  26. //     id: string;
  27. //     name: string;
  28. // }
  29. // 添加属性修饰符
  30. type Concrete<T> = {
  31.     [P in keyof T]-?: T[P];
  32. };
  33. type MaybeUser = {
  34.     id: string;
  35.     name?: string;
  36.     age?: number;
  37. };
  38. type User = Concrete<MaybeUser>;
  39. // 等同于:
  40. // {
  41. //     id: string;
  42. //     name: string;
  43. //     age: number;
  44. // }
  45. // 通过as重新映射键
  46. type Getters<T> = {
  47.     [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
  48. };
  49. interface Person {
  50.     name: string;
  51.     age: number;
  52.     location: string;
  53. }
  54. type LazyPerson = Getters<Person>;
  55. // 等同于:
  56. // {
  57. //     getName: () => string;
  58. //     getAge: () => number;
  59. //     getLocation: () => string;
  60. // }
复制代码

4.4 模板字面量类型

模板字面量类型允许基于字符串操作创建类型:
  1. // 基本模板字面量类型
  2. type EmailLocaleIDs = "welcome_email" | "email_heading";
  3. type FooterLocaleIDs = "footer_title" | "footer_sendoff";
  4. type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
  5. // 等同于:
  6. // "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
  7. // 字符串联合类型中的交叉
  8. type Email = "email";
  9. type Locale = "en" | "de" | "fr";
  10. type EmailLocaleIDs = `${Email}_${Locale}`;
  11. // 等同于:
  12. // "email_en" | "email_de" | "email_fr"
  13. // 大小写转换
  14. type Greeting = "Hello, World";
  15. type ShoutyGreeting = Uppercase<Greeting>;
  16. // 等同于:
  17. // "HELLO, WORLD"
  18. type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`;
  19. type MainID = ASCIICacheKey<"my_app">;
  20. // 等同于:
  21. // "ID-MY_APP"
  22. // 字符串操作类型
  23. type Uncapitalize<S extends string> = intrinsic;
  24. type Capitalize<S extends string> = intrinsic;
  25. type Uppercase<S extends string> = intrinsic;
  26. type Lowercase<S extends string> = intrinsic;
复制代码

4.5 类型推断与控制流分析

TypeScript的类型推断和控制流分析能力非常强大:
  1. // 类型推断
  2. let x = 3;  // 推断为number类型
  3. // 上下文类型
  4. window.onmousedown = function(mouseEvent) {
  5.     console.log(mouseEvent.button);  // 推断为MouseEvent类型
  6. };
  7. // 类型保护与控制流分析
  8. function padLeft(value: string, padding: string | number) {
  9.     if (typeof padding === "number") {
  10.         return Array(padding + 1).join(" ") + value;
  11.     }
  12.     return padding + value;
  13. }
  14. // 可辨识联合与控制流分析
  15. interface Square {
  16.     kind: "square";
  17.     size: number;
  18. }
  19. interface Rectangle {
  20.     kind: "rectangle";
  21.     width: number;
  22.     height: number;
  23. }
  24. interface Circle {
  25.     kind: "circle";
  26.     radius: number;
  27. }
  28. type Shape = Square | Rectangle | Circle;
  29. function area(s: Shape): number {
  30.     switch (s.kind) {
  31.         case "square": return s.size * s.size;
  32.         case "rectangle": return s.width * s.height;
  33.         case "circle": return Math.PI * s.radius ** 2;
  34.         default:
  35.             // 如果没有处理所有情况,这里会报错
  36.             const _exhaustiveCheck: never = s;
  37.             return _exhaustiveCheck;
  38.     }
  39. }
  40. // 类型推断与null检查
  41. function doSomething(x: string | null) {
  42.     if (x === null) {
  43.         // 在这个块中,x的类型是null
  44.     } else {
  45.         // 在这个块中,x的类型是string
  46.         console.log(x.toUpperCase());
  47.     }
  48. }
  49. // 类型推断与赋值分析
  50. let x: string | number | boolean;
  51. x = "hello";  // x现在是string类型
  52. x = 42;       // x现在是number类型
  53. x = true;     // x现在是boolean类型
复制代码

5. 工程实践

5.1 配置文件详解

TypeScript的配置文件tsconfig.json是项目中的关键文件:
  1. {
  2.   "compilerOptions": {
  3.     /* 基本选项 */
  4.     "target": "es5",                          // 指定ECMAScript目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'
  5.     "module": "commonjs",                     // 指定模块代码生成: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'
  6.     "lib": ["es6", "dom"],                    // 指定要包含在编译中的库文件
  7.     "allowJs": true,                          // 允许编译javascript文件
  8.     "checkJs": true,                          // 在.js文件中报告错误
  9.     "jsx": "react",                           // 指定JSX代码生成: 'preserve', 'react-native', or 'react'
  10.     "declaration": true,                      // 生成相应的 '.d.ts' 文件
  11.     "sourceMap": true,                        // 生成相应的 '.map' 文件
  12.     "outFile": "./",                          // 连接输出到单个文件
  13.     "outDir": "./",                           // 重定向输出结构到目录
  14.     "rootDir": "./",                          // 指定输入文件的基本目录
  15.     "removeComments": true,                   // 不输出注释到输出
  16.     "noEmit": true,                           // 不输出文件
  17.     "importHelpers": true,                    // 从tslib导入辅助工具函数
  18.     "downlevelIteration": true,               // 在ES3和ES5中完全支持迭代
  19.     "isolatedModules": true,                  // 将每个文件作为单独的模块
  20.     "strict": true,                           // 启用所有严格类型检查选项
  21.    
  22.     /* 额外检查 */
  23.     "noUnusedLocals": true,                   // 报告未使用的局部变量的错误
  24.     "noUnusedParameters": true,               // 报告未使用的参数的错误
  25.     "noImplicitReturns": true,               // 在函数中没有隐式返回时报告错误
  26.     "noFallthroughCasesInSwitch": true,       // 在switch语句中报告fallthrough情况的错误
  27.    
  28.     /* 模块解析选项 */
  29.     "moduleResolution": "node",               // 指定模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
  30.     "baseUrl": "./",                          // 用于解析非相对模块名称的基本目录
  31.     "paths": {},                              // 模块名到基于baseUrl的路径映射
  32.     "rootDirs": [],                           // 根文件夹列表,其组合内容表示项目运行时的内容结构
  33.     "typeRoots": [],                          // 包含类型声明的文件夹列表
  34.     "types": [],                              // 要包含的类型声明文件名列表
  35.     "allowSyntheticDefaultImports": true,     // 允许从没有默认导出的模块中默认导入
  36.    
  37.     /* Source Map Options */
  38.     "sourceRoot": "",                         // 指定调试器应定位的位置,而不是生成的源位置
  39.     "mapRoot": "",                            // 指定调试器应定位映射文件的位置,而不是生成的位置
  40.     "inlineSourceMap": true,                  // 生成带有源映射的单个文件,而不是单独的文件
  41.     "inlineSources": true,                    // 将源代码与源映射一起内联到单个文件中
  42.    
  43.     /* 实验性选项 */
  44.     "experimentalDecorators": true,           // 启用对ES装饰器的实验性支持
  45.     "emitDecoratorMetadata": true            // 为装饰器发出设计类型元数据
  46.   },
  47.   "include": [
  48.     "src/**/*"
  49.   ],
  50.   "exclude": [
  51.     "node_modules",
  52.     "**/*.spec.ts"
  53.   ]
  54. }
复制代码

5.2 与框架集成

TypeScript可以与现代前端框架无缝集成:
  1. // 安装类型定义
  2. npm install --save-dev @types/react @types/react-dom
  3. // 基本函数组件
  4. import React from 'react';
  5. interface GreetingProps {
  6.   name: string;
  7. }
  8. const Greeting: React.FC<GreetingProps> = ({ name }) => {
  9.   return <h1>Hello, {name}!</h1>;
  10. };
  11. export default Greeting;
  12. // 类组件
  13. import React from 'react';
  14. interface CounterState {
  15.   count: number;
  16. }
  17. class Counter extends React.Component<{}, CounterState> {
  18.   constructor(props: {}) {
  19.     super(props);
  20.     this.state = {
  21.       count: 0
  22.     };
  23.   }
  24.   increment = () => {
  25.     this.setState({ count: this.state.count + 1 });
  26.   };
  27.   render() {
  28.     return (
  29.       <div>
  30.         <p>Count: {this.state.count}</p>
  31.         <button onClick={this.increment}>Increment</button>
  32.       </div>
  33.     );
  34.   }
  35. }
  36. export default Counter;
  37. // 使用Hooks
  38. import React, { useState, useEffect } from 'react';
  39. interface User {
  40.   id: number;
  41.   name: string;
  42.   email: string;
  43. }
  44. const UserProfile: React.FC<{ userId: number }> = ({ userId }) => {
  45.   const [user, setUser] = useState<User | null>(null);
  46.   const [loading, setLoading] = useState<boolean>(true);
  47.   useEffect(() => {
  48.     const fetchUser = async () => {
  49.       try {
  50.         setLoading(true);
  51.         const response = await fetch(`https://api.example.com/users/${userId}`);
  52.         const userData = await response.json();
  53.         setUser(userData);
  54.       } catch (error) {
  55.         console.error('Error fetching user:', error);
  56.       } finally {
  57.         setLoading(false);
  58.       }
  59.     };
  60.     fetchUser();
  61.   }, [userId]);
  62.   if (loading) return <div>Loading...</div>;
  63.   if (!user) return <div>User not found</div>;
  64.   return (
  65.     <div>
  66.       <h2>{user.name}</h2>
  67.       <p>Email: {user.email}</p>
  68.     </div>
  69.   );
  70. };
  71. export default UserProfile;
复制代码
  1. // 安装类型定义
  2. npm install --save-dev vue-class-component vue-property-decorator
  3. // 使用装饰器
  4. import { Vue, Component, Prop } from 'vue-property-decorator';
  5. @Component
  6. export default class Greeting extends Vue {
  7.   @Prop({ type: String, required: true })
  8.   name!: string;
  9.   get greeting(): string {
  10.     return `Hello, ${this.name}!`;
  11.   }
  12. }
  13. // 使用Composition API
  14. import { defineComponent, ref, computed } from 'vue';
  15. export default defineComponent({
  16.   props: {
  17.     name: {
  18.       type: String,
  19.       required: true
  20.     }
  21.   },
  22.   setup(props) {
  23.     const count = ref(0);
  24.    
  25.     const doubleCount = computed(() => count.value * 2);
  26.    
  27.     function increment() {
  28.       count.value++;
  29.     }
  30.    
  31.     return {
  32.       count,
  33.       doubleCount,
  34.       increment
  35.     };
  36.   }
  37. });
  38. // 使用TypeScript与Vue 3的<script setup>
  39. <script setup lang="ts">
  40. import { ref, computed } from 'vue';
  41. interface Props {
  42.   name: string;
  43.   initialCount?: number;
  44. }
  45. const props = withDefaults(defineProps<Props>(), {
  46.   initialCount: 0
  47. });
  48. const count = ref(props.initialCount);
  49. const doubleCount = computed(() => count.value * 2);
  50. function increment() {
  51.   count.value++;
  52. }
  53. </script>
复制代码

5.3 代码规范与最佳实践

使用TypeScript时,遵循一些最佳实践可以提高代码质量和可维护性:
  1. // 1. 使用明确的类型而不是any
  2. // 不推荐
  3. function processData(data: any) {
  4.   return data.map(item => item.id);
  5. }
  6. // 推荐
  7. interface DataItem {
  8.   id: number;
  9.   name: string;
  10. }
  11. function processData(data: DataItem[]) {
  12.   return data.map(item => item.id);
  13. }
  14. // 2. 使用接口定义对象形状
  15. // 不推荐
  16. function printUser(user: { name: string; age: number }) {
  17.   console.log(`${user.name} is ${user.age} years old`);
  18. }
  19. // 推荐
  20. interface User {
  21.   name: string;
  22.   age: number;
  23. }
  24. function printUser(user: User) {
  25.   console.log(`${user.name} is ${user.age} years old`);
  26. }
  27. // 3. 使用类型别名简化复杂类型
  28. // 不推荐
  29. function processItems(items: Array<{ id: number; name: string; tags: string[] }>) {
  30.   // ...
  31. }
  32. // 推荐
  33. type Item = {
  34.   id: number;
  35.   name: string;
  36.   tags: string[];
  37. };
  38. function processItems(items: Item[]) {
  39.   // ...
  40. }
  41. // 4. 使用泛型提高代码复用性
  42. // 不推荐
  43. function getNumberArrayLength(arr: number[]): number {
  44.   return arr.length;
  45. }
  46. function getStringArrayLength(arr: string[]): number {
  47.   return arr.length;
  48. }
  49. // 推荐
  50. function getArrayLength<T>(arr: T[]): number {
  51.   return arr.length;
  52. }
  53. // 5. 使用枚举代替魔法字符串
  54. // 不推荐
  55. function setStatus(status: string) {
  56.   if (status === 'active') {
  57.     // ...
  58.   } else if (status === 'inactive') {
  59.     // ...
  60.   }
  61. }
  62. // 推荐
  63. enum Status {
  64.   Active = 'active',
  65.   Inactive = 'inactive',
  66.   Pending = 'pending'
  67. }
  68. function setStatus(status: Status) {
  69.   if (status === Status.Active) {
  70.     // ...
  71.   } else if (status === Status.Inactive) {
  72.     // ...
  73.   }
  74. }
  75. // 6. 使用类型守卫缩小类型范围
  76. // 不推荐
  77. function processValue(value: string | number) {
  78.   if (typeof value === 'string') {
  79.     console.log(value.toUpperCase());
  80.   } else {
  81.     console.log(value.toFixed(2));
  82.   }
  83. }
  84. // 推荐
  85. function isString(value: unknown): value is string {
  86.   return typeof value === 'string';
  87. }
  88. function processValue(value: string | number) {
  89.   if (isString(value)) {
  90.     console.log(value.toUpperCase());
  91.   } else {
  92.     console.log(value.toFixed(2));
  93.   }
  94. }
  95. // 7. 使用readonly和const确保不可变性
  96. // 不推荐
  97. let config = {
  98.   apiUrl: 'https://api.example.com',
  99.   timeout: 5000
  100. };
  101. config.timeout = 10000; // 可能导致意外修改
  102. // 推荐
  103. interface Config {
  104.   readonly apiUrl: string;
  105.   readonly timeout: number;
  106. }
  107. const config: Config = {
  108.   apiUrl: 'https://api.example.com',
  109.   timeout: 5000
  110. };
  111. // 8. 使用工具类型简化类型操作
  112. // 不推荐
  113. interface PartialUser {
  114.   id?: number;
  115.   name?: string;
  116.   email?: string;
  117. }
  118. // 推荐
  119. interface User {
  120.   id: number;
  121.   name: string;
  122.   email: string;
  123. }
  124. type PartialUser = Partial<User>;
复制代码

5.4 调试技巧

在TypeScript中进行调试时,有一些技巧可以帮助你更有效地解决问题:
  1. // 1. 使用类型断言进行调试
  2. function processValue(value: unknown) {
  3.   // 使用类型断言检查类型
  4.   if ((value as string).toUpperCase) {
  5.     console.log("It's a string");
  6.   } else if ((value as number).toFixed) {
  7.     console.log("It's a number");
  8.   }
  9. }
  10. // 2. 使用条件类型进行类型检查
  11. type IsString<T> = T extends string ? true : false;
  12. type CheckString = IsString<"hello">; // true
  13. type CheckNumber = IsString<42>; // false
  14. // 3. 使用类型守卫进行运行时检查
  15. function isString(value: unknown): value is string {
  16.   return typeof value === 'string';
  17. }
  18. function processValue(value: unknown) {
  19.   if (isString(value)) {
  20.     // 在这个块中,TypeScript知道value是string类型
  21.     console.log(value.toUpperCase());
  22.   }
  23. }
  24. // 4. 使用类型谓词进行高级类型检查
  25. interface Bird {
  26.   fly(): void;
  27.   layEggs(): void;
  28. }
  29. interface Fish {
  30.   swim(): void;
  31.   layEggs(): void;
  32. }
  33. function isFish(pet: Fish | Bird): pet is Fish {
  34.   return (pet as Fish).swim !== undefined;
  35. }
  36. function processPet(pet: Fish | Bird) {
  37.   if (isFish(pet)) {
  38.     pet.swim();
  39.   } else {
  40.     pet.fly();
  41.   }
  42. }
  43. // 5. 使用映射类型进行调试
  44. type Optional<T> = {
  45.   [K in keyof T]?: T[K];
  46. };
  47. interface User {
  48.   id: number;
  49.   name: string;
  50.   email: string;
  51. }
  52. type PartialUser = Optional<User>;
  53. // 6. 使用条件类型和infer进行类型推断
  54. type Unpack<T> = T extends Array<infer U> ? U : T;
  55. type UnpackedString = Unpack<string[]>; // string
  56. type UnpackedNumber = Unpack<number>; // number
  57. // 7. 使用类型断言和类型保护处理DOM元素
  58. function getElementById<T extends HTMLElement>(id: string): T | null {
  59.   const element = document.getElementById(id);
  60.   return element as T;
  61. }
  62. const button = getElementById<HTMLButtonElement>('my-button');
  63. if (button) {
  64.   button.disabled = true;
  65. }
  66. // 8. 使用类型查询获取变量类型
  67. const user = {
  68.   id: 1,
  69.   name: 'John',
  70.   email: 'john@example.com'
  71. };
  72. type User = typeof user;
复制代码

6. 实战项目

6.1 构建一个完整的TypeScript应用

让我们构建一个简单的任务管理应用,展示TypeScript的实际应用:
  1. // 1. 定义类型和接口
  2. interface Task {
  3.   id: number;
  4.   title: string;
  5.   description: string;
  6.   completed: boolean;
  7.   createdAt: Date;
  8.   updatedAt: Date;
  9. }
  10. interface TaskService {
  11.   getAll(): Promise<Task[]>;
  12.   getById(id: number): Promise<Task | null>;
  13.   create(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task>;
  14.   update(id: number, updates: Partial<Task>): Promise<Task | null>;
  15.   delete(id: number): Promise<boolean>;
  16. }
  17. // 2. 实现任务服务
  18. class LocalStorageTaskService implements TaskService {
  19.   private readonly STORAGE_KEY = 'tasks';
  20.   async getAll(): Promise<Task[]> {
  21.     const tasksJson = localStorage.getItem(this.STORAGE_KEY);
  22.     if (!tasksJson) return [];
  23.    
  24.     const tasks = JSON.parse(tasksJson);
  25.     return tasks.map((task: any) => ({
  26.       ...task,
  27.       createdAt: new Date(task.createdAt),
  28.       updatedAt: new Date(task.updatedAt)
  29.     }));
  30.   }
  31.   async getById(id: number): Promise<Task | null> {
  32.     const tasks = await this.getAll();
  33.     return tasks.find(task => task.id === id) || null;
  34.   }
  35.   async create(taskData: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Promise<Task> {
  36.     const tasks = await this.getAll();
  37.     const newId = tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1;
  38.    
  39.     const newTask: Task = {
  40.       id: newId,
  41.       ...taskData,
  42.       createdAt: new Date(),
  43.       updatedAt: new Date()
  44.     };
  45.    
  46.     tasks.push(newTask);
  47.     this.saveTasks(tasks);
  48.    
  49.     return newTask;
  50.   }
  51.   async update(id: number, updates: Partial<Task>): Promise<Task | null> {
  52.     const tasks = await this.getAll();
  53.     const taskIndex = tasks.findIndex(task => task.id === id);
  54.    
  55.     if (taskIndex === -1) return null;
  56.    
  57.     tasks[taskIndex] = {
  58.       ...tasks[taskIndex],
  59.       ...updates,
  60.       updatedAt: new Date()
  61.     };
  62.    
  63.     this.saveTasks(tasks);
  64.     return tasks[taskIndex];
  65.   }
  66.   async delete(id: number): Promise<boolean> {
  67.     const tasks = await this.getAll();
  68.     const filteredTasks = tasks.filter(task => task.id !== id);
  69.    
  70.     if (filteredTasks.length === tasks.length) return false;
  71.    
  72.     this.saveTasks(filteredTasks);
  73.     return true;
  74.   }
  75.   private saveTasks(tasks: Task[]): void {
  76.     localStorage.setItem(this.STORAGE_KEY, JSON.stringify(tasks));
  77.   }
  78. }
  79. // 3. 创建任务管理器类
  80. class TaskManager {
  81.   constructor(private taskService: TaskService) {}
  82.   async addTask(title: string, description: string): Promise<Task> {
  83.     return this.taskService.create({
  84.       title,
  85.       description,
  86.       completed: false
  87.     });
  88.   }
  89.   async completeTask(id: number): Promise<Task | null> {
  90.     return this.taskService.update(id, { completed: true });
  91.   }
  92.   async updateTask(id: number, updates: Partial<Omit<Task, 'id' | 'createdAt' | 'updatedAt'>>): Promise<Task | null> {
  93.     return this.taskService.update(id, updates);
  94.   }
  95.   async removeTask(id: number): Promise<boolean> {
  96.     return this.taskService.delete(id);
  97.   }
  98.   async getTasks(): Promise<Task[]> {
  99.     return this.taskService.getAll();
  100.   }
  101.   async getTask(id: number): Promise<Task | null> {
  102.     return this.taskService.getById(id);
  103.   }
  104. }
  105. // 4. 创建UI控制器
  106. class TaskUIController {
  107.   private taskList: HTMLElement;
  108.   private taskForm: HTMLFormElement;
  109.   private taskTitleInput: HTMLInputElement;
  110.   private taskDescriptionInput: HTMLTextAreaElement;
  111.   
  112.   constructor(
  113.     private taskManager: TaskManager,
  114.     private container: HTMLElement
  115.   ) {
  116.     this.setupUI();
  117.     this.attachEventListeners();
  118.     this.loadTasks();
  119.   }
  120.   private setupUI(): void {
  121.     this.container.innerHTML = `
  122.       <div class="task-app">
  123.         <h1>Task Manager</h1>
  124.         <form id="task-form" class="task-form">
  125.           <div class="form-group">
  126.             <label for="task-title">Title</label>
  127.             <input type="text" id="task-title" required>
  128.           </div>
  129.           <div class="form-group">
  130.             <label for="task-description">Description</label>
  131.             <textarea id="task-description" rows="3"></textarea>
  132.           </div>
  133.           <button type="submit">Add Task</button>
  134.         </form>
  135.         <div id="task-list" class="task-list"></div>
  136.       </div>
  137.     `;
  138.     this.taskList = document.getElementById('task-list')!;
  139.     this.taskForm = document.getElementById('task-form')! as HTMLFormElement;
  140.     this.taskTitleInput = document.getElementById('task-title')! as HTMLInputElement;
  141.     this.taskDescriptionInput = document.getElementById('task-description')! as HTMLTextAreaElement;
  142.   }
  143.   private attachEventListeners(): void {
  144.     this.taskForm.addEventListener('submit', (e) => {
  145.       e.preventDefault();
  146.       this.handleAddTask();
  147.     });
  148.   }
  149.   private async handleAddTask(): Promise<void> {
  150.     const title = this.taskTitleInput.value.trim();
  151.     const description = this.taskDescriptionInput.value.trim();
  152.    
  153.     if (!title) return;
  154.    
  155.     try {
  156.       await this.taskManager.addTask(title, description);
  157.       this.taskForm.reset();
  158.       this.loadTasks();
  159.     } catch (error) {
  160.       console.error('Error adding task:', error);
  161.     }
  162.   }
  163.   private async loadTasks(): Promise<void> {
  164.     try {
  165.       const tasks = await this.taskManager.getTasks();
  166.       this.renderTasks(tasks);
  167.     } catch (error) {
  168.       console.error('Error loading tasks:', error);
  169.     }
  170.   }
  171.   private renderTasks(tasks: Task[]): void {
  172.     this.taskList.innerHTML = '';
  173.    
  174.     if (tasks.length === 0) {
  175.       this.taskList.innerHTML = '<p>No tasks found. Add your first task!</p>';
  176.       return;
  177.     }
  178.    
  179.     tasks.forEach(task => {
  180.       const taskElement = document.createElement('div');
  181.       taskElement.className = `task-item ${task.completed ? 'completed' : ''}`;
  182.       taskElement.innerHTML = `
  183.         <div class="task-header">
  184.           <h3>${this.escapeHtml(task.title)}</h3>
  185.           <div class="task-actions">
  186.             <button class="toggle-task" data-id="${task.id}">
  187.               ${task.completed ? 'Mark Incomplete' : 'Mark Complete'}
  188.             </button>
  189.             <button class="delete-task" data-id="${task.id}">Delete</button>
  190.           </div>
  191.         </div>
  192.         <p>${this.escapeHtml(task.description)}</p>
  193.         <div class="task-meta">
  194.           <small>Created: ${task.createdAt.toLocaleString()}</small>
  195.           ${task.updatedAt > task.createdAt ? `<small>Updated: ${task.updatedAt.toLocaleString()}</small>` : ''}
  196.         </div>
  197.       `;
  198.       
  199.       this.taskList.appendChild(taskElement);
  200.     });
  201.    
  202.     // Add event listeners to buttons
  203.     document.querySelectorAll('.toggle-task').forEach(button => {
  204.       button.addEventListener('click', (e) => {
  205.         const id = parseInt((e.target as HTMLElement).getAttribute('data-id')!);
  206.         this.handleToggleTask(id);
  207.       });
  208.     });
  209.    
  210.     document.querySelectorAll('.delete-task').forEach(button => {
  211.       button.addEventListener('click', (e) => {
  212.         const id = parseInt((e.target as HTMLElement).getAttribute('data-id')!);
  213.         this.handleDeleteTask(id);
  214.       });
  215.     });
  216.   }
  217.   private async handleToggleTask(id: number): Promise<void> {
  218.     try {
  219.       const task = await this.taskManager.getTask(id);
  220.       if (task) {
  221.         await this.taskManager.updateTask(id, { completed: !task.completed });
  222.         this.loadTasks();
  223.       }
  224.     } catch (error) {
  225.       console.error('Error toggling task:', error);
  226.     }
  227.   }
  228.   private async handleDeleteTask(id: number): Promise<void> {
  229.     try {
  230.       await this.taskManager.removeTask(id);
  231.       this.loadTasks();
  232.     } catch (error) {
  233.       console.error('Error deleting task:', error);
  234.     }
  235.   }
  236.   private escapeHtml(text: string): string {
  237.     const div = document.createElement('div');
  238.     div.textContent = text;
  239.     return div.innerHTML;
  240.   }
  241. }
  242. // 5. 初始化应用
  243. document.addEventListener('DOMContentLoaded', () => {
  244.   const container = document.getElementById('app')!;
  245.   const taskService = new LocalStorageTaskService();
  246.   const taskManager = new TaskManager(taskService);
  247.   const uiController = new TaskUIController(taskManager, container);
  248. });
复制代码

6.2 常见问题与解决方案

在使用TypeScript的过程中,你可能会遇到一些常见问题。以下是一些解决方案:
  1. // 问题代码
  2. const user = JSON.parse('{"name": "John", "age": 30}');
  3. console.log(user.name); // 错误: 类型"{}"上不存在属性"name"
  4. // 解决方案1: 类型断言
  5. const user = JSON.parse('{"name": "John", "age": 30}') as { name: string; age: number };
  6. console.log(user.name);
  7. // 解决方案2: 接口定义
  8. interface User {
  9.   name: string;
  10.   age: number;
  11. }
  12. const user = JSON.parse('{"name": "John", "age": 30}') as User;
  13. console.log(user.name);
  14. // 解决方案3: 类型守卫
  15. function isUser(obj: any): obj is User {
  16.   return typeof obj === 'object' &&
  17.          obj !== null &&
  18.          typeof obj.name === 'string' &&
  19.          typeof obj.age === 'number';
  20. }
  21. const user = JSON.parse('{"name": "John", "age": 30}');
  22. if (isUser(user)) {
  23.   console.log(user.name);
  24. }
复制代码
  1. // 问题代码
  2. interface User {
  3.   name: string;
  4.   email?: string;
  5. }
  6. function getEmail(user: User): string {
  7.   return user.email.toLowerCase(); // 错误: 对象可能为"undefined"
  8. }
  9. // 解决方案1: 使用可选链操作符
  10. function getEmail(user: User): string {
  11.   return user.email?.toLowerCase() || 'No email provided';
  12. }
  13. // 解决方案2: 使用类型守卫
  14. function getEmail(user: User): string {
  15.   if (user.email) {
  16.     return user.email.toLowerCase();
  17.   }
  18.   return 'No email provided';
  19. }
  20. // 解决方案3: 使用默认值
  21. function getEmail(user: User): string {
  22.   const email = user.email || 'No email provided';
  23.   return email.toLowerCase();
  24. }
复制代码
  1. // 问题代码
  2. function fetchData() {
  3.   return fetch('/api/data')
  4.     .then(response => response.json())
  5.     .then(data => {
  6.       return data.items; // 错误: 类型"{}"上不存在属性"items"
  7.     });
  8. }
  9. // 解决方案1: 定义返回类型
  10. interface DataItem {
  11.   id: number;
  12.   name: string;
  13. }
  14. interface ApiResponse {
  15.   items: DataItem[];
  16. }
  17. async function fetchData(): Promise<DataItem[]> {
  18.   const response = await fetch('/api/data');
  19.   const data = (await response.json()) as ApiResponse;
  20.   return data.items;
  21. }
  22. // 解决方案2: 使用泛型
  23. async function fetchData<T>(): Promise<T> {
  24.   const response = await fetch('/api/data');
  25.   return response.json() as Promise<T>;
  26. }
  27. // 使用示例
  28. interface DataItem {
  29.   id: number;
  30.   name: string;
  31. }
  32. interface ApiResponse {
  33.   items: DataItem[];
  34. }
  35. async function getData() {
  36.   const response = await fetchData<ApiResponse>();
  37.   return response.items;
  38. }
复制代码
  1. // 问题代码
  2. function getProperty(obj: any, key: string) {
  3.   return obj[key]; // 返回类型为any
  4. }
  5. // 解决方案1: 使用泛型和keyof
  6. function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  7.   return obj[key];
  8. }
  9. // 使用示例
  10. interface User {
  11.   name: string;
  12.   age: number;
  13. }
  14. const user: User = { name: 'John', age: 30 };
  15. const userName = getProperty(user, 'name'); // 类型为string
  16. const userAge = getProperty(user, 'age');   // 类型为number
  17. const invalid = getProperty(user, 'email'); // 错误: 类型"email"不存在于类型"User"中
  18. // 解决方案2: 使用索引签名
  19. interface StringMap {
  20.   [key: string]: string;
  21. }
  22. function getValues(map: StringMap, keys: string[]): string[] {
  23.   return keys.map(key => map[key]);
  24. }
复制代码
  1. // 问题代码
  2. import _ from 'lodash';
  3. const result = _.map([1, 2, 3], n => n * 2); // 错误: 找不到模块"lodash"或其相应的类型声明
  4. // 解决方案1: 安装类型定义
  5. npm install --save-dev @types/lodash
  6. // 解决方案2: 创建自己的类型声明
  7. // 在项目中创建types/lodash.d.ts文件
  8. declare module 'lodash' {
  9.   export function map<T, U>(array: T[], iteratee: (item: T) => U): U[];
  10.   // 添加其他你使用的lodash函数...
  11. }
  12. // 解决方案3: 使用require和类型断言
  13. const _ = require('lodash') as any;
  14. const result = _.map([1, 2, 3], n => n * 2);
复制代码

总结

TypeScript是一个强大的工具,它通过添加静态类型系统和其他特性来扩展JavaScript。本指南从基础语法开始,逐步介绍了TypeScript的高级特性和实际应用。

通过学习本指南,你已经了解了:

1. TypeScript的基本类型和语法
2. 接口、类和泛型的使用
3. 中级特性如高级类型、类型守卫和装饰器
4. 高级特性如条件类型、映射类型和模板字面量类型
5. 工程实践,包括配置文件、框架集成和最佳实践
6. 实战项目开发

要真正掌握TypeScript,最重要的是实践。尝试将TypeScript应用到你的项目中,逐步采用其特性,你会发现代码的可维护性和可靠性会显著提高。

TypeScript的生态系统不断发展,新的特性和改进不断推出。保持学习,关注TypeScript的最新发展,将帮助你充分利用这个强大的工具。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

频道订阅

频道订阅

加入社群

加入社群

联系我们|TG频道|RSS

Powered by Pixtech

© 2025 Pixtech Team.