我可以强制 TypeScript 编译器使用名义类型吗?

2024-04-23

TypeScript 使用结构子类型 https://www.typescriptlang.org/docs/handbook/type-compatibility.html,所以这实际上是可能的:

// there is a class
class MyClassTest {
    foo():void{}
    bar():void{}
}

// and a function which accepts instances of that class as a parameter
function someFunction(f:MyClassTest):void{
    if (f){
        if (!(f instanceof MyClassTest)) {
            console.log("why")
        } 
    }
}

// but it's valid to pass objects, which aren't in fact instances
// they just need to "look" the same, be "structural subtypes"
someFunction({foo(){}, bar(){}})  //valid!

然而作为实施提供者someFunction我真的想禁止传递结构相似的对象,但我真的只想允许 MyClassTest 或其子类型的真实实例。我想至少对我自己的一些函数声明强制执行“名义类型”。

这可能吗?

背景:考虑传递给 API 的对象需要是该类型的情况,例如因为它们是由工厂生产的,该工厂在该对象上设置一些内部状态,并且该对象实际上有一个私有接口someFunction期望在那里以便正常工作。但是,我不想透露该私有接口(例如在打字稿定义文件中),但我希望如果有人传递虚假实现,它会成为编译器错误。具体示例:我希望打字稿编译器在这种情况下抱怨,即使我提供了所有成员,如下所示:

//OK for typescript, breaks at runtime
window.document.body.appendChild({nodeName:"div", namespaceURI:null, ...document.body}) 

没有编译器标志可以使编译器以名义方式运行,实际上原则上不可能有这样的标志,因为这会破坏很多 JavaScript 场景。

通常有几种技术用于模拟某种名义打字。品牌类型通常用于基元(从here https://stackoverflow.com/search?q=user%3A125734+branded+types作为示例),对于类,常见的方法是添加私有字段(私有字段在结构上不会与除了确切的私有字段定义之外的任何内容匹配)。

使用最后一种方法,您的初始样本可以写为:

// there is a class
class MyClassTest {
    private _useNominal: undefined; // private field to ensure nothing else is structually compatible.
    foo():void{}
    bar():void{}
}

// and a function which accepts instances of that class as a parameter
function someFunction(f:MyClassTest):void{
    if (f){
        if (!(f instanceof MyClassTest)) {
            console.log("why")
        } 
    }
}


someFunction({foo(){}, bar(){}, _useNominal: undefined})  //err

对于您的具体场景,使用append我不认为我们能做任何我们能做的事Node定义于lib.d.ts作为一个接口和一个const,因此用私有字段来增强它几乎是不可能的(不改变lib.d.ts),即使我们可以,它也可能会破坏许多现有的代码和定义。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我可以强制 TypeScript 编译器使用名义类型吗? 的相关文章

随机推荐