【问题标题】:Can I force the TypeScript compiler to use nominal typing?我可以强制 TypeScript 编译器使用名义类型吗?
【发布时间】:2019-03-21 13:08:48
【问题描述】:

TypeScript 使用structural subtyping,所以这实际上是可能的:

// 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}) 

【问题讨论】:

    标签: typescript duck-typing


    【解决方案1】:

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

    有几种技术通常用于模拟某种名义类型。品牌类型通常用于原语(从here 中选择一个作为示例),对于类,一种常见的方法是添加私有字段(私有字段在结构上不会与除确切的私有字段定义之外的任何内容匹配)。

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

    // 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 我不认为我们可以做任何我们可以做的事情,因为Nodelib.d.ts 中被定义为一个接口和一个const,所以用一个私有的来增加它字段几乎是不可能的(不更改lib.d.ts),即使我们可以,它也可能会破坏许多现有的代码和定义。

    【讨论】:

    • 谢谢!我实际上并不是在寻找全局编译器标志。很明显,这样的标志可能会破坏很多代码。我希望能够为参数类型添加某种修饰符(例如someFunction(f:MyClassTest!)),以仅针对该方法更改行为。 “私有” hack 很酷 - 但我想它不适用于类型定义文件,因为私有定义没有什么意义。
    • @Sebastian 您可以在声明中使用私有字段,这没有问题,因为 TS 需要了解私有字段以防止意外覆盖。但在这种情况下,没有办法扩充现有定义。
    • 实际上,像 strictNullChecks => nominalTypeChecks 这样的全局(选择加入)标志是可行的,我猜 - 我不相信它会比 strictNullCheck 破坏更多的代码。无论如何,interfaces 仍然是鸭式的。我不认为这些 fake-_class_es 使用得这么频繁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    相关资源
    最近更新 更多