【问题标题】:Is there a way of providing polymorphic type discrimination similar to Rust's enums?有没有办法提供类似于 Rust 枚举的多态类型区分?
【发布时间】:2020-12-12 20:45:01
【问题描述】:

在用 Rust 编写了很多代码后,我最近回到了 JS/TS,这是我非常怀念的一个功能。

假设我有一个值,它可能是Cat 的实例或Dog 的实例,并且根据它来自哪个类,我想执行不同的逻辑。由于 Javascript 无法做到classname ==(我认为?),我通常依赖的也是传递一个指示类的字符串,如下所示:

function groomAnimal(animal: Cat | Dog, animalType: 'cat' | 'dog') {
    if (animalType === 'cat') {
        (animal as Cat).trimNails();
    } else {
        (animal as Dog).trimFur();
    }
}

我讨厌这个,因为它很笨拙,编译器不能保证调用者正确地履行合同。另一方面,在 Rust 中,这将非常优雅:

enum Animal {
    Cat(CatData),
    Dog(DogData),
}

fn groomAnimal(animal: Animal) {
    match animal {
        Cat(catData) => catData.trimNails(),
        Dog(dogData) => dogData.trimFur(),
    }
}

基本上,表示“类”的enum本身也持有“类”数据,因此获取数据和检查数据类型是相同的操作

打字稿中是否有任何模式允许多态函数签名,同时确保在编译时代码对正确的数据类型进行操作?

【问题讨论】:

    标签: javascript typescript polymorphism


    【解决方案1】:

    保留一个单独的值来指示类型很容易出错。如果CatDog是类类型(不仅仅是接口),可以直接使用instanceof来验证其类型:

    class Cat {
        trimNails() {}
    }
    
    class Dog {
        trimFur() {}
    }
    
    function groomAnimal(animal: Cat | Dog) {
        if (animal instanceof Cat) {
            // animal must be Cat, so you can call trimNails()
            animal.trimNails();
        } else {
            // animal must otherwise be a Dog, so you can call trimFur()
            animal.trimFur();
        }
    }
    

    【讨论】:

    • 不幸的是它们是接口。它们是从从服务器加载的 JSON 解析的。我认为没有一种简单的方法可以让它们上课而不产生一些开销
    • 但实际上,这给了我一个想法。我基本上可以将接口包装在类中并使用instanceof,这实际上与 rust 解决方案非常相似。我认为这比我所做的要好
    【解决方案2】:

    您可以使用Cat/Dog 和一些标识符属性创建一个可组合的结构,这与您的方法有点相似但有所不同。

    类似的,

    type Animal = Cat & { animalType: 'Cat' } | Dog & { animalType: 'Dog' }
    

    您的groomAnimal 方法现在将采用组合结构,

    function groomAnimal(animal: Animal) {
        if (animal.animalType === 'Cat') {
            animal.trimNails();
        } else {
           animal.trimFur();
        }
    }
    

    这是一个小示例,我在其中创建了一只猫并将其传递给 groomAnimal 方法,

    const tom = {
        trimNails() {
            console.log('Trimming nails')
        }
    }
    
    groomAnimal({ ...tom, kind: 'Cat'})
    

    在你的情况下,你说你从一些 api 调用中解析这些。您可以在解析时准备好 Animal/Animals[] 对象,然后在修饰时使用它。

    当然,编译器可以保证调用者确实正确地履行了合同,正如你从代码中看到的那样。

    【讨论】:

    • 我知道这已经很老了,但回想起来这绝对是正确的解决方案。我认为我当时并不欣赏它,但它非常干净和直接
    猜你喜欢
    • 1970-01-01
    • 2011-01-07
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多