简体   繁体   English

模式匹配 switch 语句

[英]Pattern matching switch statements

Suppose I have APIs that return information about animals.假设我有返回动物信息的 API。 However, the json payload varies greatly for each kind of animal, even if many properties are common and mandatory.然而,json 有效载荷对于每种动物来说差异很大,即使许多属性是共同的和强制性的。

I want to have 'strongly typed' typescript class for each of these different animals so my code doesn't become a mess.我想为这些不同的动物中的每一种都“强输入”typescript class,这样我的代码就不会变得一团糟。 Each animal requires very distinct and specific handling!每只动物都需要非常独特和特定的处理方式!

What is the right way of doing this?这样做的正确方法是什么? Basically I'd like to accomplish something like this:基本上我想完成这样的事情:

interface Animal {
    Name: string;
    Weight: number;
}

interface Insect extends Animal {
    AmountOfEyes: number;
}

interface Bird extends Animal {
    PlumageColor : string;
}

function OnlyForBirds(bird: Bird)
{
     // do something birdly
}

function OnlyForInsects(insect: Insect)
{
     // do something creepy
}


function GetAnimal(animalId: string) : Promise<Animal>
{
    const uri = `${baseURL}/${animalId}`;

    // fetches the json response body from http request
    const result = await get<any>(uri); 

    switch(animal.Name)
    {
        case  'Insect':
            return result as Insect;
        case ...
            ...
    }

    // throw unhandled
}

function ProcessAnimal(animalId:string) : Promise
{
    let animal = await GetAnimal(animalId);
 
    // how do I do this now? Can't I use something over tye interface
    // types instead of using the .Name and casting again?
    // is there any advisable standard I can use?

    if(animal is a bird){  
        OnlyForBirds(bird)
    }

    else if(animal is an insect){
        OnlyForInsects(insect)
    }
}

Any suggestions including not using interfaces like this are appreciated.任何建议,包括不使用这样的界面,都值得赞赏。

For your use case, the answer that you posted is probably the best solution.对于您的用例,您发布的答案可能是最好的解决方案。 I just want to chime in with a different approach.我只是想用不同的方法来插话。 Your solution starts to break down if you want to have multiple layers of inheritance, where Duck extends Bird .如果您想拥有多层 inheritance,您的解决方案开始崩溃,其中Duck扩展了Bird If you want to find if an Animal matches the base Bird interface, you can define a custom type guard function that looks at the properties of the object to see if it has a PlumageColor .如果要查找Animal是否与基本Bird接口匹配,可以定义自定义类型保护function 来查看 object 的属性以查看它是否具有PlumageColor If it does, then typescript knows that it's ok to use it as a Bird .如果是这样,那么 typescript 知道可以将其用作Bird

Here is the basic version.这是基本版本。 We say that animal has an optional property PlumageColor so that we can access it without error even when it's undefined .我们说animal有一个可选的属性PlumageColor以便我们可以无错误地访问它,即使它是undefined Then we check that PlumageColor is defined and it's a string .然后我们检查PlumageColor是否已定义并且它是一个string

const isBird = (animal: Animal & {PlumageColor?: any}): animal is Bird => {
  return typeof animal.PlumageColor === "string";
}

This version with generics is better because it asserts that animal is Bird while also preserving any other type information that was already know about the animal .这个带有generics的版本更好,因为它断言animalBird同时还保留了已经知道的关于animal的任何其他类型信息。

const isBird = <T extends Animal & {PlumageColor?: any}>(animal: T): animal is T & Bird => {
  return typeof animal.PlumageColor === "string";
}

I figured it out.我想到了。 Some deep dark magic.一些深邃的黑暗魔法。 Define an enum proprery in the base interface.在基本接口中定义一个枚举属性。 Each new different type of class, or animal in this case, assigns itself one of these properties.每个新的不同类型的 class,或在这种情况下为动物,都为自己分配这些属性之一。 Switch on this property and within the switch you have your typed animal打开这个属性,在开关中你有你输入的动物

enum AnimalType {
    Insect = "Insect",
    Bird = "Bird"
}

interface Animal {
    Type: AnimalType;
    Weight: number;
}

interface Insect extends Animal {
    Type: AnimalType.Insect; // MAGIC BREWING
    AmountOfEyes: number;
}

interface Bird extends Animal {
    Type: AnimalType.Bird; // MAGIC BREWING
    PlumageColor : string;
}

function OnlyForBirds(bird: Bird)
{
     // do something birdly
}

function OnlyForInsects(insect: Insect)
{
     // do something creepy
}


function GetAnimal(animalId: string) : Promise<Animal>
{
    const uri = `${baseURL}/${animalId}`;

    // fetches the json response body from http request
    const result = await get<any>(uri); 

    switch(animal.Type)
    {
        case  'Insect':
            return result as Insect;
        case ...
            ...
    }

    // throw unhandled
}

function ProcessAnimal(animalId:string) : Promise
{
    let animal = await GetAnimal(animalId);

    switch(animal.AnimalType){
        case AnimalType.Insect:
            OnlyForInsects(animal); // within this case clause, animal is typed as an Insect! MAGIC!
            break;
        
        case AnimalType.Bird:
            OnlyForBirds(animal); // within this case clause, animal is typed as an bird! Go free little bird!
            break;

        default:
            // throw
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM