TypeScript中如何定义可new的构造函数类型以约束Tree及其派生类
JS代码对应TypeScript实现与treeMaker参数类型定义
基础类的TypeScript 实现
首先完成原有类的TS类型补全,让类的属性和方法有明确的类型定义:
class Tree { name: string; type: string; constructor(name: string) { this.name = name; this.type = 'unspecified'; } grow() { console.log(`${this.name} (${this.type}): growing.`); } } class Pine extends Tree { constructor(name: string) { super(name); this.type = 'pine'; } somethinPiney(): boolean { return true; } }
可实例化构造函数的TS声明语法
TypeScript中要定义“支持new调用的构造器”类型,核心是使用构造签名,和普通函数类型的区别是在签名前加new修饰符,基本格式为:new (...入参列表: 参数类型) => 构造后的实例类型
没有new修饰的函数类型仅表示普通可调用函数,不允许直接通过new操作符调用。
约束treeMaker仅接收Tree及派生类构造器的写法
可以根据需求精度选择两种实现方式:
基础实现(返回值为通用Tree类型)
直接通过构造签名约束参数范围,只要构造器满足“接收一个string参数、new后返回Tree实例”的要求就可以传入:
function treeMaker(treeType: new (name: string) => Tree, name: string): Tree { return new treeType(name); }
这个写法天然兼容所有继承自Tree的派生类构造器——因为TS的结构兼容规则中,子类构造器返回的子类实例可以赋值给父类型Tree的位置。
如果传入
Date、Array这类和Tree无关的构造器,TS会直接抛出类型错误,无法通过编译。
优化实现(泛型保留精确子类类型)
如果希望不丢失传入构造器的实际实例类型(比如传入Pine构造器时,返回值是Pine类型,可以直接调用Pine独有的somethinPiney方法),可以通过泛型实现更精准的类型约束:
// 通过 T extends Tree 约束泛型必须是Tree本身或其子类 function treeMaker<T extends Tree>(treeType: new (name: string) => T, name: string): T { return new treeType(name); }
这个写法的优势是:
- 从类型层面禁止传入非Tree系的构造器,不符合
T extends Tree约束的构造器会被直接拦截 - 自动推导和保留实际的实例类型,不需要手动做类型断言
运行效果
原JS的测试代码可以完全在TS环境中直接运行,行为和原JS一致:
const t = new Tree('Noname'); const p = new Pine('Backyard'); const m = treeMaker(Pine, 'Seaside'); t.grow(); p.grow(); m.grow(); console.log(m instanceof Pine); // true console.log(m instanceof Tree); // true console.log(m instanceof Date); // false
内容的提问来源于stack exchange,提问作者Passiday
相关产品推荐
相关产品推荐

