本文献给:
已掌握 TypeScript 接口、类实现接口等知识的开发者。本文将带你系统学习 TypeScript 中类的核心语法,包括字段、构造函数、方法、访问修饰符(public/private/protected)、只读属性以及参数属性(构造器简写),为后续继承、多态、抽象类等内容打下基础。
你将学到:
- 类的字段、构造函数与方法定义
public、private、protected的区别与使用场景readonly只读属性- 参数属性(Parameter Properties)简化代码
- 类的类型注解与实例化
目录
- 一、类的定义
-
- [1.1 基本语法](#1.1 基本语法)
- [1.2 字段初始化](#1.2 字段初始化)
- 二、访问修饰符
-
- [2.1 public(默认)](#2.1 public(默认))
- [2.2 private](#2.2 private)
- [2.3 protected](#2.3 protected)
- [三、readonly 只读属性](#三、readonly 只读属性)
- [四、参数属性(Parameter Properties)](#四、参数属性(Parameter Properties))
-
- [4.1 传统写法 vs 参数属性](#4.1 传统写法 vs 参数属性)
- [4.2 可与其他修饰符组合](#4.2 可与其他修饰符组合)
- 五、类作为类型
- 六、常见错误与注意事项
-
- [6.1 未初始化的属性](#6.1 未初始化的属性)
- [6.2 `private` 与 `#` 混淆](#` 混淆)
- [6.3 参数属性顺序](#6.3 参数属性顺序)
- [6.4 子类访问 `protected` 成员](#6.4 子类访问
protected成员) - [6.5 类类型与实例类型](#6.5 类类型与实例类型)
- 七、综合示例
- 八、小结
一、类的定义
TypeScript 的类在 ES6 类语法的基础上增加了类型注解和访问修饰符。
1.1 基本语法
typescript
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
const alice = new Person("Alice", 25);
console.log(alice.greet());
1.2 字段初始化
字段可以在声明时赋初始值。
typescript
class Counter {
count: number = 0;
label: string = "Counter";
increment() {
this.count++;
}
}
二、访问修饰符
TypeScript 提供了三种访问修饰符,控制类成员的可见性。
2.1 public(默认)
如果不写修饰符,成员默认为 public,可以在任何地方访问。
typescript
class Animal {
public name: string;
public constructor(name: string) {
this.name = name;
}
public speak() {
console.log(`${this.name} makes a sound`);
}
}
const a = new Animal("Dog");
console.log(a.name); // 外部可访问
a.speak();
2.2 private
private 成员只能在类内部访问,不能在子类或外部访问。
typescript
class BankAccount {
private balance: number = 0;
deposit(amount: number) {
this.balance += amount;
}
getBalance() {
return this.balance;
}
}
const account = new BankAccount();
account.deposit(100);
// console.log(account.balance); // ❌ 私有属性,外部不可访问
console.log(account.getBalance()); // 100
TypeScript 的 private 是编译时检查,编译后的 JavaScript 中并没有真正的私有字段(除非使用 ES2022 的 # 私有字段)。如果希望运行时的真正私有,可以使用 #。
typescript
class WithHashPrivate {
#secret = 42;
reveal() {
return this.#secret;
}
}
2.3 protected
protected 成员可以在类内部和子类中访问,但不能在外部访问。
typescript
class Parent {
protected value: string = "protected";
}
class Child extends Parent {
show() {
console.log(this.value); // ✅ 子类可访问
}
}
const c = new Child();
c.show();
// console.log(c.value); // ❌ 外部不可访问
三、readonly 只读属性
readonly 修饰符让属性只能在声明时或构造函数中被赋值,之后不可修改。
typescript
class Config {
readonly appName: string;
readonly version: string = "1.0.0";
constructor(appName: string) {
this.appName = appName; // 构造函数中赋值允许
}
changeName() {
// this.appName = "New"; // ❌ 不可修改
}
}
const cfg = new Config("MyApp");
console.log(cfg.appName);
// cfg.appName = "Other"; // ❌
readonly 与访问修饰符可以同时使用,通常写在修饰符后面:
typescript
class Example {
public readonly id: number;
protected readonly createdAt: Date;
private readonly secret: string;
constructor(id: number, secret: string) {
this.id = id;
this.createdAt = new Date();
this.secret = secret;
}
}
四、参数属性(Parameter Properties)
TypeScript 提供了一种简写方式,可以在构造函数参数中直接声明并初始化类属性。在参数前加上访问修饰符或 readonly,TypeScript 会自动创建同名字段并赋值。
4.1 传统写法 vs 参数属性
typescript
// 传统写法
class UserOld {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// 参数属性写法
class User {
constructor(public name: string, public age: number) {}
}
const u = new User("Alice", 25);
console.log(u.name); // "Alice"
4.2 可与其他修饰符组合
typescript
class Service {
constructor(
private readonly apiUrl: string,
protected timeout: number,
public debug: boolean = false
) {}
}
参数属性大大简化了代码,是 TypeScript 类中的常见模式。
五、类作为类型
在 TypeScript 中,类同时创建了两个东西 :一个值 (构造函数,在运行时存在)和一个类型(实例的类型,在编译时存在)。
typescript
class Point {
x: number = 0;
y: number = 0;
}
// 作为类型
const p1: Point = new Point();
// 作为值
const PointClass = Point; // 将构造函数赋给变量
const p2 = new PointClass();
因此,类可以像接口一样用于类型注解。
typescript
function printPoint(p: Point) {
console.log(p.x, p.y);
}
六、常见错误与注意事项
6.1 未初始化的属性
如果属性没有初始值,也不在构造函数中赋值,TypeScript(在严格模式下)会报错。
typescript
class MyClass {
field: string; // ❌ 属性未初始化
}
解决方法:赋初始值、构造函数中赋值,或使用非空断言 !(不推荐)。
typescript
class MyClass {
field!: string; // 明确断言会初始化
}
6.2 private 与 # 混淆
TypeScript 的 private 只提供编译时检查,编译后是普通属性。真正的运行时私有字段使用 #。
typescript
class C {
private x = 0;
#y = 0;
}
// 编译后,x 变成 this.x,仍然可访问;#y 会转为 WeakMap 或使用原生私有字段
6.3 参数属性顺序
参数属性必须放在构造函数参数的最前面(或与普通参数混用时没有严格顺序要求,但通常建议集中放在前面或后面)。使用参数属性后,不能再显式写 this.xxx = xxx。
6.4 子类访问 protected 成员
子类只能通过自身实例访问 protected 成员,不能通过父类实例访问。
typescript
class Parent {
protected value = 42;
}
class Child extends Parent {
tryAccess(parent: Parent, child: Child) {
console.log(this.value); // OK
console.log(child.value); // OK
// console.log(parent.value); // ❌ 不能通过父类实例访问
}
}
6.5 类类型与实例类型
类名作为类型时,指的是实例的类型,不是构造函数类型。要获取构造函数类型,使用 typeof MyClass。
typescript
const ctor: typeof MyClass = MyClass;
七、综合示例
typescript
// 定义一个用户类
class User {
// 参数属性 + 只读
constructor(
public readonly id: number,
public name: string,
private email: string,
protected role: string = "user"
) {}
// 方法
getEmail(): string {
return this.email;
}
setEmail(newEmail: string) {
// 简单验证
if (newEmail.includes("@")) {
this.email = newEmail;
} else {
console.log("Invalid email");
}
}
describe(): string {
return `${this.name} (${this.role})`;
}
}
// 子类
class Admin extends User {
private permissions: string[];
constructor(id: number, name: string, email: string) {
super(id, name, email, "admin");
this.permissions = ["read", "write", "delete"];
}
listPermissions() {
console.log(this.permissions);
}
// 可以访问 protected 成员 role
getRole(): string {
return this.role;
}
}
// 使用
const user = new User(1, "Alice", "alice@example.com");
console.log(user.id); // 1
console.log(user.name); // "Alice"
// console.log(user.email); // ❌ private
console.log(user.getEmail()); // "alice@example.com"
user.setEmail("new@example.com");
const admin = new Admin(2, "Bob", "bob@admin.com");
admin.listPermissions();
console.log(admin.getRole()); // "admin"
// 类作为类型
function printUserInfo(u: User) {
console.log(u.describe());
}
printUserInfo(user);
printUserInfo(admin); // 子类实例也可传入
// 获取构造函数类型
type UserConstructor = typeof User;
const AnotherUser: UserConstructor = User;
const u2 = new AnotherUser(3, "Charlie", "charlie@example.com");
八、小结
| 概念 | 语法示例 | 说明 |
|---|---|---|
| 字段声明 | name: string; |
定义实例属性 |
| 构造函数 | constructor(...) {} |
初始化实例 |
| 方法 | greet() {} |
实例方法 |
public |
public name: string |
默认修饰符,任何地方可访问 |
private |
private secret: string |
仅类内部可访问 |
protected |
protected value: number |
类内部及子类可访问 |
readonly |
readonly id: number |
只读,不可修改 |
| 参数属性 | constructor(public name: string) {} |
简写声明并赋值 |
| 类作为类型 | let p: MyClass = new MyClass() |
类名表示实例类型 |
觉得文章有帮助?别忘了:
👍 点赞 👍 -- 给我一点鼓励
⭐ 收藏 ⭐ -- 方便以后查看
🔔 关注 🔔 -- 获取更新通知
标签: #TypeScript #类 #构造函数 #访问修饰符 #学习笔记 #前端开发