在代码片段中,函数 doSomething
应该只接受 Base 类型的对象,而不接受其他匹配类型结构的类型,例如 instanceOf
约束。
interface IBase {
prop1: string;
}
abstract class Base implements IBase {
constructor() { }
prop1: string = '';
}
class Derived1 extends Base { }
class Derived2 extends Base { }
class AnotherBase {
constructor() { }
prop1: string = '';
}
// T extends Base
function doSomething<T extends Base>(obj: T) {
// do something
}
//cases should work
doSomething(new Derived1());
doSomething(new Derived2());
// cases should NOT work
doSomething({ prop1: 'test' }); // works
doSomething(new AnotherBase()); // works
我们怎样才能让函数严格只接受 Base 类型的对象? 我们可以理解当前按照 type-compatibility 规则工作。但是,是否有一种方法可以指定通用约束以匹配精确类型?
更新:
从这个和类似问题的答案来看,实现这个编译时似乎是不可能的。我决定包含 instanceOf
检查以在运行时实现预期行为。
// T extends Base
function doSomething<T extends Base>(obj: T) {
if(!( obj instanceof Base)){
console.log('not a Base type');
throw new Error('An instance of Base is expected.');
}
// do something
}
//cases should work
console.log('Derived1');
doSomething(new Derived1());
console.log('Derived2');
doSomething(new Derived2());
// cases should NOT work
console.log('{..}');
doSomething({ prop1: 'test' }); // doesn't work
console.log('AnotherBase');
doSomething(new AnotherBase()); // doesn't work
答案 0 :(得分:2)
简短回答:不。
打字稿是结构性的,而不是名义上的。这使得如果两种类型具有相同的接口,那么它们就是相同的类型。这甚至包括类实例与对象字面量。
我们可以用类似的东西来测试:
type Exact<A, B> = A extends B ? B extends A ? true : false : false
这个类型别名需要两个参数。如果 A 扩展了 B,反之亦然,那么我们知道 typescript 认为它们是相同的。
type IBase_Base = Exact<IBase, Base> // true
type Base_Derived = Exact<Base, Derived> // true
type Derived_ObjLiertal = Exact<Base, { prop1: string }> // true
因此,就打字稿而言,class Derived extends Base { }
与 Base
相同。事实上,只要实例具有与文字相同的形状,类实例和对象文字就被认为是相同的。
也就是说,如果派生类在任何方面都不同,那么您可以在帮助中注意到如果此 Exact
类型并禁止它。
首先,您可以创建一个类型来强制类型完全匹配,例如:
type EnforceExact<Constraint, T> = Exact<Constraint, T> extends true ? T : never
然后让你的函数使用这个助手:
function doSomething<T extends IBase>(obj: EnforceExact<IBase, T>) {
// do something
}
现在,如果我们以某种方式使 Derived
类不同:
class DerivedWithAddition extends Base {
foo: number = 123
}
然后当我们尝试传入这个时,我们会得到一个类型错误:
doSomething(new DerivedWithAddition()) // not assignable to parameter of type 'never'.
您也可以使用 the answer that @kaya3 linked 中建议的 brand 解决方法。但是,除非有很好的理由,否则您可能不应该这样做。这种结构等效性是打字稿的一个非常棒的特性。只要有正确的界面,只要让某人传递他们想要的任何东西,你的生活就会更轻松。