如何强制TypeScript函数仅接收类实例而非结构匹配的普通对象?
问题描述
下面的TypeScript代码编译时无报错,但运行时会抛出错误:
class Foo { foo = ''; } class Bar { bar = 0; } function getNumber(value: Foo | Bar): number { if (value instanceof Bar) { return value.bar; } else { return value.foo.length; } } console.log(getNumber({bar: 1}));
原因是{bar: 1}只是结构匹配Bar的普通对象,并非Bar类的实例,instanceof Bar判断不成立后会进入else分支,尝试访问不存在的foo属性,导致运行时错误。
需求是:强制getNumber仅接收Foo或Bar类的实例,拒绝结构匹配的普通对象。调用getNumber({bar: 1})时,希望在编译阶段就报错,同时运行时能抛出明确提示。
解决方案
1. 编译阶段阻止结构匹配
TypeScript采用结构类型系统,默认只要属性结构匹配就会被视为兼容类型。要打破这种兼容,可给类添加私有属性或唯一Symbol属性,普通对象无法拥有这些属性,编译时就会报错。
方案一:添加私有属性
class Foo { private readonly _isInstance = true; foo = ''; } class Bar { private readonly _isInstance = true; bar = 0; } function getNumber(value: Foo | Bar): number { if (value instanceof Bar) { return value.bar; } else { return value.foo.length; } } // 编译报错:类型"{ bar: number; }"缺少私有属性"_isInstance" getNumber({bar: 1}); // 合法调用 const bar = new Bar(); bar.bar = 1; console.log(getNumber(bar));
方案二:添加唯一Symbol属性
const instanceSymbol = Symbol('class-instance'); class Foo { [instanceSymbol] = true; foo = ''; } class Bar { [instanceSymbol] = true; bar = 0; } function getNumber(value: Foo | Bar): number { if (value instanceof Bar) { return value.bar; } else { return value.foo.length; } } // 编译报错:类型"{ bar: number; }"缺少属性"[instanceSymbol]" getNumber({bar: 1});
2. 运行时增强错误提示
保留instanceof判断的同时,添加兜底的错误抛出逻辑,确保即使绕过编译检查(比如类型断言),运行时也能给出明确提示:
function getNumber(value: Foo | Bar): number { if (value instanceof Bar) { return value.bar; } else if (value instanceof Foo) { return value.foo.length; } else { throw new Error('预期传入Foo或Bar类的实例,实际传入了普通对象'); } }
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

