本文献给:
已掌握 TypeScript 类基础、继承、静态成员等知识的开发者。本文将带你理解类在 TypeScript 中的双重身份(值和类型),学习如何将类作为类型注解、获取构造函数类型,以及类类型接口的常见用法。
你将学到:
- 类同时创建值和类型的原理
- 将类作为类型注解使用
typeof MyClass获取构造函数类型- 类类型与接口的互换
- 实际场景:依赖注入、工厂函数
目录
- 一、类的双重身份
- 二、类作为类型注解
-
- [2.1 类与接口的互换](#2.1 类与接口的互换)
- [三、获取构造函数类型:`typeof MyClass`](#三、获取构造函数类型:
typeof MyClass) -
- [3.1 更精确的构造函数类型](#3.1 更精确的构造函数类型)
- [3.2 类中的静态部分与实例部分](#3.2 类中的静态部分与实例部分)
- 四、类类型与工厂模式
- 五、类作为接口使用
- 六、常见错误与注意事项
-
- [6.1 混淆实例类型与构造函数类型](#6.1 混淆实例类型与构造函数类型)
- [6.2 使用 `this` 作为返回类型](#6.2 使用
this作为返回类型) - [6.3 类与接口的兼容性陷阱](#6.3 类与接口的兼容性陷阱)
- [6.4 泛型类中的静态成员](#6.4 泛型类中的静态成员)
- 七、综合示例
- 八、小结
一、类的双重身份
在 TypeScript 中,声明一个类会创建两个东西:
- 值:构造函数(运行时存在)
- 类型:实例的类型(编译时存在)
typescript
class Point {
x: number = 0;
y: number = 0;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
distance(): number {
return Math.hypot(this.x, this.y);
}
}
// Point 作为类型(实例类型)
const p1: Point = new Point(3, 4);
// Point 作为值(构造函数)
const PointClass = Point; // 将构造函数赋给变量
const p2 = new PointClass(1, 2);
这种双重身份是 TypeScript 特有的,因为 JavaScript 类本身就是构造函数(值),而 TypeScript 额外添加了类型信息。
二、类作为类型注解
类名可以直接用作类型,表示该类的实例。
typescript
class User {
name: string;
constructor(name: string) {
this.name = name;
}
greet() {
return `Hello, ${this.name}`;
}
}
function printUser(user: User) {
console.log(user.greet());
}
const alice = new User("Alice");
printUser(alice); // OK
// 对象字面量只要结构匹配也可以
const bob = { name: "Bob", greet: () => "Hi Bob" };
printUser(bob); // OK(结构化类型兼容)
注意:TypeScript 是结构化类型系统,只要对象形状匹配,即使不是通过 new User 创建,也可以赋值给 User 类型。
2.1 类与接口的互换
类和接口在类型层面可以互相兼容,因为接口只描述形状,类也描述形状。
typescript
interface Person {
name: string;
age: number;
}
class Student {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const s: Person = new Student("Alice", 20); // OK,类实例可赋给接口类型
三、获取构造函数类型:typeof MyClass
有时我们需要表示构造函数本身的类型(而不是实例类型)。使用 typeof 类名可以获取构造函数类型。
typescript
class Point {
constructor(public x: number, public y: number) {}
}
type PointConstructor = typeof Point;
// 等价于: new (x: number, y: number) => Point
const ctor: PointConstructor = Point;
const p = new ctor(10, 20); // p 类型为 Point
构造函数类型可以用于参数类型,表示需要接收一个类的构造函数。
typescript
function createInstance<T>(ctor: new (...args: any[]) => T, ...args: any[]): T {
return new ctor(...args);
}
class Animal {
constructor(public name: string) {}
}
const animal = createInstance(Animal, "Dog");
console.log(animal.name); // "Dog"
3.1 更精确的构造函数类型
可以显式写出构造签名的类型。
typescript
interface PointConstructor {
new (x: number, y: number): Point;
}
function makePoint(ctor: PointConstructor, x: number, y: number): Point {
return new ctor(x, y);
}
3.2 类中的静态部分与实例部分
typeof MyClass 得到的类型包含了类的所有静态成员和构造函数,而实例类型只包含实例成员。
typescript
class Service {
static version = "1.0";
name: string = "service";
static getVersion() {
return this.version;
}
getName() {
return this.name;
}
}
type ServiceInstance = Service; // 实例类型:{ name: string; getName(): string }
type ServiceStatic = typeof Service; // 静态类型:{ version: string; getVersion(): string; new(): Service }
const s: ServiceInstance = new Service();
const ctor: ServiceStatic = Service;
console.log(ctor.version); // "1.0"
四、类类型与工厂模式
利用构造函数类型可以实现类型安全的工厂函数。
typescript
interface Product {
getPrice(): number;
}
class Book implements Product {
constructor(public title: string, private price: number) {}
getPrice() { return this.price; }
}
class Electronic implements Product {
constructor(public name: string, private price: number) {}
getPrice() { return this.price; }
}
class ProductFactory {
static create<T extends Product>(
ctor: new (...args: any[]) => T,
...args: any[]
): T {
return new ctor(...args);
}
}
const book = ProductFactory.create(Book, "TypeScript Guide", 29.99);
const laptop = ProductFactory.create(Electronic, "Laptop", 999);
console.log(book.getPrice());
console.log(laptop.getPrice());
五、类作为接口使用
因为类定义了一个形状,你可以用类来约束其他对象(类似于接口)。这在某些场景下可以减少重复定义。
typescript
class Config {
url: string = "";
timeout: number = 5000;
retries: number = 3;
}
// 不需要单独定义接口
function init(config: Config) {
console.log(config.url);
}
// 传入普通对象,只要形状匹配即可
init({ url: "https://api.com", timeout: 3000, retries: 2 });
但通常建议:如果不需要构造函数和具体实现,专门定义接口更清晰。
六、常见错误与注意事项
6.1 混淆实例类型与构造函数类型
typescript
class Foo {}
let a: Foo = Foo; // ❌ Foo 是构造函数,不能赋值给实例类型
let b: typeof Foo = Foo; // ✅
6.2 使用 this 作为返回类型
类方法可以返回 this 类型,表示返回当前实例(支持链式调用,且在子类中自动适配)。
typescript
class Animal {
name: string = "";
setName(name: string): this {
this.name = name;
return this;
}
}
class Dog extends Animal {
bark(): this {
console.log(`${this.name} barks`);
return this;
}
}
const dog = new Dog().setName("Buddy").bark(); // 链式调用,类型正确
6.3 类与接口的兼容性陷阱
由于结构化类型系统,即使类中有私有字段,也不会阻止类型兼容(但私有字段在目标类型中必须也存在)。
typescript
class A {
private x = 1;
}
class B {
private x = 1;
}
const a: A = new B(); // ❌ 私有成员不兼容
6.4 泛型类中的静态成员
如之前所说,静态成员不能引用类的泛型参数。同样,typeof 在泛型类中也需要小心。
typescript
class Box<T> {
value: T;
constructor(value: T) { this.value = value; }
}
// type BoxCtor<T> = typeof Box<T>; // ❌ 不能这样写
可以使用泛型函数来传递构造函数。
七、综合示例
typescript
// 定义一个模型基类
abstract class Model {
id: number = 0;
abstract tableName(): string;
save() {
console.log(`Saving to ${this.tableName()}`);
return this;
}
}
// 用户模型
class User extends Model {
name: string = "";
email: string = "";
tableName() { return "users"; }
}
// 订单模型
class Order extends Model {
total: number = 0;
tableName() { return "orders"; }
}
// 仓库,接收构造函数类型,可以创建和存储模型实例
class Repository<T extends Model> {
private items: T[] = [];
constructor(private ctor: new () => T) {}
create(): T {
const instance = new this.ctor();
instance.id = this.items.length + 1;
this.items.push(instance);
return instance;
}
findAll(): T[] {
return this.items;
}
}
// 使用
const userRepo = new Repository(User);
const user1 = userRepo.create();
user1.name = "Alice";
user1.save();
const orderRepo = new Repository(Order);
const order1 = orderRepo.create();
order1.total = 299;
order1.save();
// 获取构造函数类型示例
type UserConstructor = typeof User;
const Ctor: UserConstructor = User;
const user2 = new Ctor();
user2.name = "Bob";
console.log(user2.name);
// 类作为类型注解
function printModel(m: Model) {
console.log(`Model id: ${m.id}, table: ${m.tableName()}`);
}
printModel(user1);
printModel(order1);
八、小结
| 概念 | 语法/示例 | 说明 |
|---|---|---|
| 类作为类型 | let p: MyClass |
表示实例类型 |
| 类作为值 | const C = MyClass |
构造函数值 |
| 构造函数类型 | typeof MyClass |
包含构造签名和静态成员 |
| 实例类型 vs 静态类型 | MyClass vs typeof MyClass |
前者是实例形状,后者是构造函数 |
| 构造签名 | new (x: number) => T |
描述可构造的函数 |
| 工厂函数参数 | (ctor: new () => T) => T |
传入构造函数,返回实例 |
this 类型 |
method(): this { return this; } |
支持链式调用,子类自动适配 |
觉得文章有帮助?别忘了:
👍 点赞 👍 -- 给我一点鼓励
⭐ 收藏 ⭐ -- 方便以后查看
🔔 关注 🔔 -- 获取更新通知
标签: #TypeScript #类的类型 #构造函数 #typeof #学习笔记 #前端开发