Huckleberry Carignan
Huckleberry Carignan

Reputation: 2318

How to use interface as a criteria to a filter in Typescript

Please forgive me, I'm a javascript developer trying to learn typescript. In a challenge I've hit a snag.

So, I've got some interfaces and a tray of objects (data) and I'm trying to use the interface as a criteria in a filter function, but the type 'Cat' is not defined. I tried exporting it, but it brought other errors.

interfaces:

interface Cat {
   type: 'cat';
   name: string;
   hunts?: string;
}

interface Fish {
   type: 'fish'
   name: string;
   hides?: string;
}

export type Animal = Cat | Fish;
export const isCat = (animal: Animal): animal is Cat => animal.type === 'cat';
export const isFish = (animal: Animal): animal is Fish => animal.type === 'fish';

data:

import { Animal} from '../interface/species';

export const pets: Animal[] = [
{
    type: 'cat',
    name: 'Mr Whiskers',
    hunts: 'mice'
},
{
    type: 'fish',
    name: 'bubbles',
    hides: 'castle'
}
];

my trouble is with this filter function:

export function filterAnimal(animal: Animal[], criteria: Cat): Cat[] {
    return animal.filter((pet) => {
        for (var key in pet) {
            if (pet[key] === criteria)
                return pet; 
        }
    });
}

How can I use Cat as the criteria?

Upvotes: 1

Views: 2114

Answers (1)

Szaman
Szaman

Reputation: 2388

Types and interfaces are a part of typescript and they get removed during complication, so you cannot access them at runtime directly.

However, you can use a property which identifies you type. In your case, it looks like each Cat has type === "cat", so in your filter you could do:

export function filterAnimal(animals: Animal[], type: "cat" | "dog"): Animal[] {
  return animals.filter((animal) => animal.type === type);
}

If you'd like a function that specifically filters Cats, you can do so similarly:

export function getOnlyCats(animals: Animal[]): Animal[] {
  return animals.filter((animal) => animal.type === "cat");
}

Now, in some cases you may need to rely on the fact that your function only returns instances of the Cat type. For example, you want to print what the cat hunts without errors. In this case, you'll need to manually assert that the function in fact returns an array of Cat objects, by casting the return type.

function getOnlyCats(animals: Animal[]): Cat[] {
  return (animals.filter((animal) => animal.type === "cat")) as Cat[];
}

// now you can safely do
const cats = getOnlyCats(animals);
cats.forEach(cat => {
  if (cat.hunts) {
    console.log(`cat ${cat.name} hunts ${cat.hunts}.`)
  } else {
    console.log(`cat ${cat.hunts} doesn't hunt.`)
  }
});

Check out a live example here.

Upvotes: 1

Related Questions