活动公告

系统通知
06-18 23:43
系统通知
06-14 00:00
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

TypeScript类属性详解从基础定义到高级应用全面掌握类型安全与面向对象编程的核心概念

SunJu_FaceMall

3万

主题

3077

科技点

3万

积分

执行版主

碾压王

积分
32876

塔罗立华奏

执行版主 发表于 2025-9-29 22:40:01 | 显示全部楼层 |阅读模式

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

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

x
引言

TypeScript作为JavaScript的超集,为我们带来了静态类型检查和更强大的面向对象编程能力。在TypeScript的众多特性中,类属性是构建健壮、可维护应用程序的核心组成部分。类属性不仅存储对象的状态,还通过类型注解和修饰符提供了强大的类型安全保障。本文将深入探讨TypeScript类属性的各个方面,从基础定义到高级应用,帮助读者全面掌握这一核心概念,提升TypeScript编程技能。

TypeScript类属性基础

类属性的基本定义

在TypeScript中,类属性是类中声明的变量,用于存储对象的状态或数据。与JavaScript不同,TypeScript允许我们为属性指定类型,从而在编译阶段进行类型检查。
  1. class Person {
  2.     name: string;  // 声明一个字符串类型的属性
  3.     age: number;  // 声明一个数字类型的属性
  4. }
  5. let person = new Person();
  6. person.name = "Alice";
  7. person.age = 30;
复制代码

在上面的例子中,我们定义了一个Person类,它有两个属性:name和age。每个属性都有明确的类型注解,确保只能赋相应类型的值。

属性类型注解

TypeScript中的类属性可以使用任何有效的类型进行注解,包括基本类型、对象类型、数组类型、函数类型等。
  1. class Car {
  2.     brand: string;                    // 字符串类型
  3.     year: number;                     // 数字类型
  4.     isElectric: boolean;              // 布尔类型
  5.     features: string[];               // 字符串数组类型
  6.     engine: { type: string; power: number }; // 对象类型
  7.     drive: (speed: number) => void;   // 函数类型
  8. }
  9. let myCar = new Car();
  10. myCar.brand = "Tesla";
  11. myCar.year = 2022;
  12. myCar.isElectric = true;
  13. myCar.features = ["Autopilot", "Full Self-Driving"];
  14. myCar.engine = { type: "Electric", power: 400 };
  15. myCar.drive = (speed: number) => {
  16.     console.log(`Driving at ${speed} mph`);
  17. };
复制代码

属性初始化

在TypeScript中,类属性可以在声明时初始化,也可以在构造函数中初始化。如果属性没有初始化,并且没有显式标记为可选属性,TypeScript会报错。
  1. class Product {
  2.     // 声明时初始化
  3.     id: number = Math.floor(Math.random() * 1000);
  4.     category: string = "General";
  5.    
  6.     // 构造函数中初始化
  7.     name: string;
  8.     price: number;
  9.    
  10.     constructor(name: string, price: number) {
  11.         this.name = name;
  12.         this.price = price;
  13.     }
  14. }
  15. const product = new Product("Laptop", 999);
  16. console.log(product.id);      // 随机生成的ID
  17. console.log(product.category); // "General"
  18. console.log(product.name);    // "Laptop"
  19. console.log(product.price);   // 999
复制代码

TypeScript 4.0+ 还支持使用类属性初始化的简写形式,当构造函数参数与类属性同名时:
  1. class Employee {
  2.     constructor(
  3.         public name: string,      // 自动创建并初始化name属性
  4.         private salary: number,   // 自动创建并初始化私有salary属性
  5.         readonly department: string // 自动创建并初始化只读department属性
  6.     ) {}
  7. }
  8. const employee = new Employee("John Doe", 50000, "Engineering");
  9. console.log(employee.name);        // "John Doe"
  10. // console.log(employee.salary);  // 错误:salary是私有的
  11. console.log(employee.department);  // "Engineering"
  12. // employee.department = "HR";    // 错误:department是只读的
复制代码

属性修饰符

TypeScript提供了几种属性修饰符,用于控制属性的可访问性和可修改性。这些修饰符包括public、private、protected和readonly。

public

public是默认的修饰符,表示属性可以在任何地方被访问。即使不显式指定,所有属性默认都是public的。
  1. class Student {
  2.     public studentId: number;  // 显式声明为public
  3.     name: string;              // 默认为public
  4.    
  5.     constructor(studentId: number, name: string) {
  6.         this.studentId = studentId;
  7.         this.name = name;
  8.     }
  9. }
  10. let student = new Student(12345, "Jane Smith");
  11. console.log(student.studentId);  // 12345
  12. console.log(student.name);       // "Jane Smith"
  13. student.name = "Jane Doe";       // 可以修改
复制代码

private

private修饰符表示属性只能在声明它的类内部访问。子类和类的外部都无法访问私有属性。
  1. class BankAccount {
  2.     private balance: number;
  3.    
  4.     constructor(initialBalance: number) {
  5.         this.balance = initialBalance;
  6.     }
  7.    
  8.     public deposit(amount: number): void {
  9.         if (amount > 0) {
  10.             this.balance += amount;
  11.             console.log(`Deposited $${amount}. New balance: $${this.balance}`);
  12.         }
  13.     }
  14.    
  15.     public getBalance(): number {
  16.         return this.balance;
  17.     }
  18. }
  19. let account = new BankAccount(1000);
  20. account.deposit(500);  // Deposited $500. New balance: $1500
  21. console.log(account.getBalance());  // 1500
  22. // console.log(account.balance);     // 错误:balance是私有的,无法在类外部访问
  23. // 尝试通过子类访问私有属性
  24. class SavingsAccount extends BankAccount {
  25.     public showBalance(): void {
  26.         // console.log(this.balance);  // 错误:无法在子类中访问父类的私有属性
  27.     }
  28. }
复制代码

protected

protected修饰符表示属性只能在声明它的类及其子类中访问。与private不同,protected属性允许在继承层次结构中访问。
  1. class Vehicle {
  2.     protected brand: string;
  3.    
  4.     constructor(brand: string) {
  5.         this.brand = brand;
  6.     }
  7.    
  8.     protected getBrand(): string {
  9.         return this.brand;
  10.     }
  11. }
  12. class Car extends Vehicle {
  13.     private model: string;
  14.    
  15.     constructor(brand: string, model: string) {
  16.         super(brand);
  17.         this.model = model;
  18.     }
  19.    
  20.     public getInfo(): string {
  21.         // 可以访问父类的protected属性和方法
  22.         return `${this.brand} ${this.model}`;
  23.     }
  24. }
  25. let myCar = new Car("Toyota", "Camry");
  26. console.log(myCar.getInfo());  // "Toyota Camry"
  27. // console.log(myCar.brand);   // 错误:无法在类外部访问protected属性
复制代码

readonly

readonly修饰符表示属性只能在初始化时或构造函数中被赋值,之后不能修改。这类似于其他语言中的常量概念。
  1. class Circle {
  2.     readonly radius: number;
  3.     readonly pi: number = 3.14159;
  4.    
  5.     constructor(radius: number) {
  6.         this.radius = radius;
  7.     }
  8.    
  9.     getArea(): number {
  10.         return this.pi * this.radius * this.radius;
  11.     }
  12. }
  13. let circle = new Circle(5);
  14. console.log(circle.getArea());  // 78.53975
  15. // circle.radius = 10;          // 错误:radius是只读的,不能修改
  16. // circle.pi = 3.14;            // 错误:pi是只读的,不能修改
复制代码

静态属性

静态属性是属于类本身而不是类实例的属性。它们使用static关键字声明,可以通过类名直接访问,而不需要创建类的实例。

静态属性定义与使用
  1. class MathUtility {
  2.     static PI: number = 3.14159;
  3.    
  4.     static calculateCircumference(radius: number): number {
  5.         return 2 * this.PI * radius;
  6.     }
  7. }
  8. // 直接通过类名访问静态属性和方法
  9. console.log(MathUtility.PI);  // 3.14159
  10. console.log(MathUtility.calculateCircumference(5));  // 31.4159
  11. // 创建实例后无法访问静态属性
  12. const mathUtil = new MathUtility();
  13. // console.log(mathUtil.PI);  // 错误:实例无法访问静态属性
复制代码

静态属性的应用场景

静态属性通常用于存储与类相关的常量、配置信息或计数器等。
  1. class User {
  2.     private static userCount: number = 0;
  3.     private static readonly MAX_USERS: number = 100;
  4.    
  5.     constructor(private name: string) {
  6.         // 每次创建新用户时增加计数器
  7.         User.userCount++;
  8.         
  9.         if (User.userCount > User.MAX_USERS) {
  10.             throw new Error(`Maximum number of users (${User.MAX_USERS}) exceeded`);
  11.         }
  12.     }
  13.    
  14.     public static getUserCount(): number {
  15.         return User.userCount;
  16.     }
  17.    
  18.     public static getMaxUsers(): number {
  19.         return User.MAX_USERS;
  20.     }
  21. }
  22. console.log(User.getUserCount());  // 0
  23. const user1 = new User("Alice");
  24. const user2 = new User("Bob");
  25. console.log(User.getUserCount());  // 2
  26. try {
  27.     // 尝试创建超过最大用户数的用户
  28.     for (let i = 0; i < 100; i++) {
  29.         new User(`User${i}`);
  30.     }
  31. } catch (error) {
  32.     console.error(error.message);  // Maximum number of users (100) exceeded
  33. }
复制代码

属性访问器

TypeScript支持使用getter和setter(也称为访问器)来控制对类属性的访问和修改。这使得我们可以在属性被访问或修改时执行额外的逻辑。

getter和setter
  1. class Employee {
  2.     private _salary: number = 0;
  3.    
  4.     // getter
  5.     get salary(): number {
  6.         return this._salary;
  7.     }
  8.    
  9.     // setter
  10.     set salary(newSalary: number) {
  11.         if (newSalary < 0) {
  12.             throw new Error("Salary cannot be negative");
  13.         }
  14.         this._salary = newSalary;
  15.     }
  16. }
  17. const employee = new Employee();
  18. employee.salary = 5000;  // 调用setter
  19. console.log(employee.salary);  // 调用getter,输出5000
  20. try {
  21.     employee.salary = -1000;  // 抛出错误:Salary cannot be negative
  22. } catch (error) {
  23.     console.error(error.message);
  24. }
复制代码

属性访问器的优势

属性访问器提供了几个优势:

1. 封装:隐藏内部实现细节,只暴露必要的接口。
2. 验证:在设置属性值时进行验证。
3. 计算属性:提供基于其他属性计算得出的值。
4. 通知机制:在属性变化时触发其他操作。
  1. class Rectangle {
  2.     private _width: number = 0;
  3.     private _height: number = 0;
  4.    
  5.     get width(): number {
  6.         return this._width;
  7.     }
  8.    
  9.     set width(value: number) {
  10.         if (value <= 0) {
  11.             throw new Error("Width must be positive");
  12.         }
  13.         this._width = value;
  14.         this.logChange("width", value);
  15.     }
  16.    
  17.     get height(): number {
  18.         return this._height;
  19.     }
  20.    
  21.     set height(value: number) {
  22.         if (value <= 0) {
  23.             throw new Error("Height must be positive");
  24.         }
  25.         this._height = value;
  26.         this.logChange("height", value);
  27.     }
  28.    
  29.     // 计算属性
  30.     get area(): number {
  31.         return this._width * this._height;
  32.     }
  33.    
  34.     get perimeter(): number {
  35.         return 2 * (this._width + this._height);
  36.     }
  37.    
  38.     private logChange(property: string, value: number): void {
  39.         console.log(`${property} changed to ${value}`);
  40.     }
  41. }
  42. const rect = new Rectangle();
  43. rect.width = 10;  // 输出:width changed to 10
  44. rect.height = 5;  // 输出:height changed to 5
  45. console.log(rect.area);      // 50
  46. console.log(rect.perimeter); // 30
复制代码

抽象类中的属性

抽象类是不能被实例化的类,它们通常作为基类,为子类提供共同的属性和方法。在抽象类中,我们可以定义抽象属性,这些属性在抽象类中没有实现,必须在子类中实现。

抽象属性定义
  1. abstract class Shape {
  2.     // 抽象属性,没有初始化
  3.     abstract color: string;
  4.    
  5.     // 普通属性,有初始化
  6.     readonly id: number = Math.floor(Math.random() * 1000);
  7.    
  8.     // 抽象方法
  9.     abstract getArea(): number;
  10.    
  11.     // 普通方法
  12.     getInfo(): string {
  13.         return `Shape [ID: ${this.id}, Color: ${this.color}]`;
  14.     }
  15. }
复制代码

抽象属性的实现
  1. class Circle extends Shape {
  2.     // 实现抽象属性
  3.     color: string;
  4.     radius: number;
  5.    
  6.     constructor(color: string, radius: number) {
  7.         super();
  8.         this.color = color;
  9.         this.radius = radius;
  10.     }
  11.    
  12.     // 实现抽象方法
  13.     getArea(): number {
  14.         return Math.PI * this.radius * this.radius;
  15.     }
  16. }
  17. class Rectangle extends Shape {
  18.     // 实现抽象属性
  19.     color: string;
  20.     width: number;
  21.     height: number;
  22.    
  23.     constructor(color: string, width: number, height: number) {
  24.         super();
  25.         this.color = color;
  26.         this.width = width;
  27.         this.height = height;
  28.     }
  29.    
  30.     // 实现抽象方法
  31.     getArea(): number {
  32.         return this.width * this.height;
  33.     }
  34. }
  35. const circle = new Circle("Red", 5);
  36. console.log(circle.getInfo());  // Shape [ID: 随机数, Color: Red]
  37. console.log(circle.getArea());  // 78.539...
  38. const rectangle = new Rectangle("Blue", 4, 6);
  39. console.log(rectangle.getInfo());  // Shape [ID: 随机数, Color: Blue]
  40. console.log(rectangle.getArea());  // 24
复制代码

类属性与接口

接口在TypeScript中用于定义对象的结构,包括类应该具有的属性和方法。类可以实现一个或多个接口,确保它们具有接口中定义的所有属性。

接口定义类属性
  1. interface IPerson {
  2.     name: string;
  3.     age: number;
  4.     email?: string;  // 可选属性
  5.     readonly id: number;  // 只读属性
  6. }
  7. class Person implements IPerson {
  8.     name: string;
  9.     age: number;
  10.     email?: string;
  11.     readonly id: number;
  12.    
  13.     constructor(id: number, name: string, age: number, email?: string) {
  14.         this.id = id;
  15.         this.name = name;
  16.         this.age = age;
  17.         this.email = email;
  18.     }
  19. }
  20. const person = new Person(1, "John", 30, "john@example.com");
  21. console.log(person.name);  // "John"
  22. // person.id = 2;  // 错误:id是只读的
复制代码

类实现接口属性

一个类可以实现多个接口,每个接口可能定义不同的属性集。
  1. interface IIdentifiable {
  2.     id: number;
  3.     getId(): number;
  4. }
  5. interface ILoggable {
  6.     log(): void;
  7. }
  8. class Product implements IIdentifiable, ILoggable {
  9.     // 实现IIdentifiable接口
  10.     id: number;
  11.    
  12.     // Product类自己的属性
  13.     name: string;
  14.     price: number;
  15.    
  16.     constructor(id: number, name: string, price: number) {
  17.         this.id = id;
  18.         this.name = name;
  19.         this.price = price;
  20.     }
  21.    
  22.     // 实现IIdentifiable接口的方法
  23.     getId(): number {
  24.         return this.id;
  25.     }
  26.    
  27.     // 实现ILoggable接口的方法
  28.     log(): void {
  29.         console.log(`Product [ID: ${this.id}, Name: ${this.name}, Price: $${this.price}]`);
  30.     }
  31. }
  32. const product = new Product(101, "Laptop", 999.99);
  33. console.log(product.getId());  // 101
  34. product.log();  // Product [ID: 101, Name: Laptop, Price: $999.99]
复制代码

高级属性模式

TypeScript提供了一些高级的属性模式,使类属性的定义更加灵活和强大。

可选属性

使用?标记的属性是可选的,可以在类实例中存在或不存在。
  1. class UserProfile {
  2.     username: string;
  3.     email: string;
  4.     age?: number;  // 可选属性
  5.     bio?: string;  // 可选属性
  6.    
  7.     constructor(username: string, email: string, age?: number, bio?: string) {
  8.         this.username = username;
  9.         this.email = email;
  10.         this.age = age;
  11.         this.bio = bio;
  12.     }
  13.    
  14.     displayInfo(): void {
  15.         console.log(`Username: ${this.username}, Email: ${this.email}`);
  16.         if (this.age !== undefined) {
  17.             console.log(`Age: ${this.age}`);
  18.         }
  19.         if (this.bio !== undefined) {
  20.             console.log(`Bio: ${this.bio}`);
  21.         }
  22.     }
  23. }
  24. const user1 = new UserProfile("alice", "alice@example.com");
  25. const user2 = new UserProfile("bob", "bob@example.com", 25, "Software developer");
  26. user1.displayInfo();
  27. // 输出:
  28. // Username: alice, Email: alice@example.com
  29. user2.displayInfo();
  30. // 输出:
  31. // Username: bob, Email: bob@example.com
  32. // Age: 25
  33. // Bio: Software developer
复制代码

只读属性

如前所述,readonly修饰符用于创建只读属性,但还有一些其他方式可以创建只读属性。
  1. class Configuration {
  2.     // 使用readonly修饰符
  3.     readonly appName: string = "MyApp";
  4.    
  5.     // 使用getter但不提供setter
  6.     private _version: string = "1.0.0";
  7.     get version(): string {
  8.         return this._version;
  9.     }
  10.    
  11.     // 使用Object.defineProperty
  12.     private _apiKey: string = "secret-key";
  13.    
  14.     constructor() {
  15.         // 使用Object.defineProperty创建只读属性
  16.         Object.defineProperty(this, 'apiKey', {
  17.             value: this._apiKey,
  18.             writable: false,
  19.             enumerable: true,
  20.             configurable: false
  21.         });
  22.     }
  23. }
  24. const config = new Configuration();
  25. console.log(config.appName);  // "MyApp"
  26. console.log(config.version);  // "1.0.0"
  27. console.log(config.apiKey);   // "secret-key"
  28. // 尝试修改只读属性
  29. // config.appName = "NewApp";  // 错误:appName是只读的
  30. // config.version = "2.0.0";   // 错误:没有setter
  31. // config.apiKey = "new-key";  // 错误:apiKey是只读的
复制代码

计算属性名

TypeScript允许使用表达式作为属性名,这在创建动态属性时非常有用。
  1. class DynamicProperties {
  2.     // 使用计算属性名
  3.     [propertyName: string]: any;
  4.    
  5.     constructor() {
  6.         // 动态设置属性
  7.         this["id"] = 123;
  8.         this["name"] = "Dynamic Object";
  9.         
  10.         // 使用变量作为属性名
  11.         const propName = "createdAt";
  12.         this[propName] = new Date();
  13.     }
  14. }
  15. const dynamicObj = new DynamicProperties();
  16. console.log(dynamicObj.id);        // 123
  17. console.log(dynamicObj.name);      // "Dynamic Object"
  18. console.log(dynamicObj.createdAt); // 当前日期时间
  19. // 添加更多动态属性
  20. dynamicObj["updatedAt"] = new Date();
  21. console.log(dynamicObj.updatedAt); // 当前日期时间
复制代码

属性装饰器

装饰器是一种特殊的声明,可以附加到类声明、方法、属性或参数上,以修改类的行为。属性装饰器用于观察、修改或替换属性定义。

装饰器基础

要使用装饰器,必须在tsconfig.json中启用experimentalDecorators选项:
  1. {
  2.     "compilerOptions": {
  3.         "experimentalDecorators": true
  4.     }
  5. }
复制代码

自定义属性装饰器
  1. // 属性装饰器函数
  2. function logProperty(target: any, propertyKey: string) {
  3.     // 保存原始属性的值
  4.     let value: any;
  5.    
  6.     // 属性getter
  7.     const getter = function() {
  8.         console.log(`Get: ${propertyKey} => ${value}`);
  9.         return value;
  10.     };
  11.    
  12.     // 属性setter
  13.     const setter = function(newVal: any) {
  14.         console.log(`Set: ${propertyKey} => ${newVal}`);
  15.         value = newVal;
  16.     };
  17.    
  18.     // 替换属性
  19.     Object.defineProperty(target, propertyKey, {
  20.         get: getter,
  21.         set: setter,
  22.         enumerable: true,
  23.         configurable: true
  24.     });
  25. }
  26. // 只读属性装饰器
  27. function readonly(target: any, propertyKey: string) {
  28.     Object.defineProperty(target, propertyKey, {
  29.         writable: false,
  30.         enumerable: true,
  31.         configurable: true
  32.     });
  33. }
  34. class Example {
  35.     @logProperty
  36.     public name: string;
  37.    
  38.     @readonly
  39.     public version: string = "1.0.0";
  40.    
  41.     constructor(name: string) {
  42.         this.name = name;
  43.     }
  44. }
  45. const example = new Example("Test");
  46. // 输出:Set: name => Test
  47. const nameValue = example.name;
  48. // 输出:Get: name => Test
  49. example.name = "Updated";
  50. // 输出:Set: name => Updated
  51. console.log(example.version);  // "1.0.0"
  52. // example.version = "2.0.0";  // 错误:version是只读的
复制代码

更复杂的属性装饰器

下面是一个更复杂的属性装饰器示例,用于验证属性值:
  1. // 验证装饰器工厂
  2. function validate(validationFn: (value: any) => boolean, errorMessage: string) {
  3.     return function(target: any, propertyKey: string) {
  4.         let value: any;
  5.         
  6.         const getter = function() {
  7.             return value;
  8.         };
  9.         
  10.         const setter = function(newVal: any) {
  11.             if (!validationFn(newVal)) {
  12.                 throw new Error(errorMessage);
  13.             }
  14.             value = newVal;
  15.         };
  16.         
  17.         Object.defineProperty(target, propertyKey, {
  18.             get: getter,
  19.             set: setter,
  20.             enumerable: true,
  21.             configurable: true
  22.         });
  23.     };
  24. }
  25. class User {
  26.     @validate(
  27.         value => typeof value === 'string' && value.length >= 3,
  28.         "Username must be at least 3 characters long"
  29.     )
  30.     public username: string;
  31.    
  32.     @validate(
  33.         value => typeof value === 'number' && value >= 18 && value <= 120,
  34.         "Age must be between 18 and 120"
  35.     )
  36.     public age: number;
  37.    
  38.     @validate(
  39.         value => typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
  40.         "Invalid email format"
  41.     )
  42.     public email: string;
  43.    
  44.     constructor(username: string, age: number, email: string) {
  45.         this.username = username;
  46.         this.age = age;
  47.         this.email = email;
  48.     }
  49. }
  50. // 有效用户
  51. const validUser = new User("john_doe", 30, "john@example.com");
  52. console.log(validUser.username);  // "john_doe"
  53. try {
  54.     // 无效用户名
  55.     const invalidUser1 = new User("jd", 30, "john@example.com");
  56. } catch (error) {
  57.     console.error(error.message);  // Username must be at least 3 characters long
  58. }
  59. try {
  60.     // 无效年龄
  61.     const invalidUser2 = new User("john_doe", 15, "john@example.com");
  62. } catch (error) {
  63.     console.error(error.message);  // Age must be between 18 and 120
  64. }
  65. try {
  66.     // 无效邮箱
  67.     const invalidUser3 = new User("john_doe", 30, "invalid-email");
  68. } catch (error) {
  69.     console.error(error.message);  // Invalid email format
  70. }
复制代码

类属性的最佳实践

命名约定

遵循一致的命名约定可以提高代码的可读性和可维护性:
  1. class UserProfile {
  2.     // 使用驼峰命名法命名普通属性
  3.     firstName: string;
  4.     lastName: string;
  5.     dateOfBirth: Date;
  6.    
  7.     // 私有属性可以使用下划线前缀
  8.     private _apiKey: string;
  9.    
  10.     // 常量属性使用全大写和下划线
  11.     static readonly MAX_LOGIN_ATTEMPTS: number = 3;
  12.    
  13.     // 布尔属性使用is或has前缀
  14.     isActive: boolean;
  15.     hasPermission: boolean;
  16.    
  17.     constructor(firstName: string, lastName: string, dateOfBirth: Date) {
  18.         this.firstName = firstName;
  19.         this.lastName = lastName;
  20.         this.dateOfBirth = dateOfBirth;
  21.         this._apiKey = "default-api-key";
  22.         this.isActive = true;
  23.         this.hasPermission = false;
  24.     }
  25. }
复制代码

属性组织

在类中组织属性可以提高代码的可读性:
  1. class Order {
  2.     // 1. 静态属性
  3.     static readonly MIN_ORDER_VALUE: number = 10;
  4.     static readonly MAX_ITEMS: number = 100;
  5.    
  6.     // 2. 公共实例属性
  7.     public id: string;
  8.     public customerId: string;
  9.     public items: OrderItem[];
  10.    
  11.     // 3. 受保护属性
  12.     protected discountRate: number = 0;
  13.    
  14.     // 4. 私有属性
  15.     private _createdAt: Date;
  16.     private _updatedAt: Date;
  17.     private _status: OrderStatus = OrderStatus.Pending;
  18.    
  19.     constructor(id: string, customerId: string, items: OrderItem[]) {
  20.         this.id = id;
  21.         this.customerId = customerId;
  22.         this.items = items;
  23.         this._createdAt = new Date();
  24.         this._updatedAt = new Date();
  25.     }
  26.    
  27.     // 方法...
  28. }
  29. enum OrderStatus {
  30.     Pending,
  31.     Processing,
  32.     Shipped,
  33.     Delivered,
  34.     Cancelled
  35. }
  36. interface OrderItem {
  37.     productId: string;
  38.     quantity: number;
  39.     price: number;
  40. }
复制代码

类型安全考虑

充分利用TypeScript的类型系统来增强属性的类型安全:
  1. // 使用联合类型
  2. type UserRole = 'admin' | 'editor' | 'viewer';
  3. class User {
  4.     role: UserRole;
  5.    
  6.     constructor(role: UserRole) {
  7.         this.role = role;
  8.     }
  9. }
  10. const admin = new User('admin');
  11. // const invalidUser = new User('guest');  // 错误:'guest'不是有效的UserRole
  12. // 使用字面量类型
  13. type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;
  14. class ApiResponse {
  15.     status: HttpStatus;
  16.     data: any;
  17.    
  18.     constructor(status: HttpStatus, data: any) {
  19.         this.status = status;
  20.         this.data = data;
  21.     }
  22. }
  23. const successResponse = new ApiResponse(200, { message: 'Success' });
  24. // const invalidResponse = new ApiResponse(300, {});  // 错误:300不是有效的HttpStatus
  25. // 使用泛型类
  26. class Repository<T> {
  27.     private items: T[] = [];
  28.    
  29.     add(item: T): void {
  30.         this.items.push(item);
  31.     }
  32.    
  33.     getAll(): T[] {
  34.         return [...this.items];
  35.     }
  36. }
  37. interface Product {
  38.     id: string;
  39.     name: string;
  40.     price: number;
  41. }
  42. const productRepository = new Repository<Product>();
  43. productRepository.add({ id: '1', name: 'Laptop', price: 999 });
  44. // productRepository.add({ id: '2', name: 'Phone' });  // 错误:缺少price属性
复制代码

总结

TypeScript类属性是构建类型安全和面向对象应用程序的核心组成部分。通过本文的详细探讨,我们了解了TypeScript类属性的各个方面:

1. 基础定义:类属性的基本定义、类型注解和初始化方式。
2. 属性修饰符:public、private、protected和readonly修饰符的使用和区别。
3. 静态属性:属于类本身而不是实例的属性,以及它们的应用场景。
4. 属性访问器:使用getter和setter控制属性访问和修改。
5. 抽象类中的属性:抽象属性的定义和实现。
6. 类属性与接口:接口如何定义类属性,以及类如何实现接口属性。
7. 高级属性模式:可选属性、只读属性和计算属性名。
8. 属性装饰器:使用装饰器观察、修改或替换属性定义。
9. 最佳实践:属性命名约定、组织方式和类型安全考虑。

掌握这些概念和技术,将帮助开发者充分利用TypeScript的类型系统和面向对象特性,构建更加健壮、可维护的应用程序。TypeScript类属性不仅提供了强大的类型安全保障,还通过修饰符、访问器和装饰器等特性,为开发者提供了灵活的工具来设计和实现复杂的类结构。

随着TypeScript的不断发展,类属性和相关特性也在不断演进。保持对最新版本特性的关注,并遵循最佳实践,将有助于开发者充分利用TypeScript的强大功能,提升代码质量和开发效率。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

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

本版积分规则