Reputation: 6968
I have two interfaces that extend a third one, for example
interface Animal { /* name, etc */ };
interface Dog extends Animal { /* dog specific things */ }
interface Cat extends Animal { /* cat specific things */ }
I want to create for the two extending classes "Model" classes, that simplify the work with the database
interface AnimalModel<A extends Animal> {
all(): Promise<A[]>;
get(id: string): Promise<A>;
create(user: A): Promise<void>;
}
I went ahead and
class DogModel implements AnimalModel<Dog> {
// Expected TypeScript to complain, but it does not, because Cat does extend the animal?
async create(cat: Cat) { /* actual implementation, query the db */ }
}
What am I doing wrong? I expected class DogModel implements AnimalModel<Dog>
methods to only accept and/or return Dog
s, but it can be any Animal
.
ps: I'm thinking about using an abstract class, so I might solve it differently, the question is still unanswered and I want to understand what is wrong with my code (the one in this question)
Upvotes: 1
Views: 36
Reputation: 6968
TypeScript didn't complain, as the Dog
and Cat
interfaces only contained optional properties. When they have different required properties, TypeScript correctly raises an error
Upvotes: 1
Reputation: 250186
Typescript uses structural compatibility to determine type compatibility, so two types with the same structure are compatible. In your case if the two interface have compatible structure, you will not get an error. With the code you posted (with empty interfaces) the two interfaces are obviously compatible, but this example would also so trigger an error:
interface Dog extends Animal { eat(): void; }
interface Cat extends Animal { eat(): void; scratch(): void; }
If the types contain incompatible fields then you will get an error, for example add a discrimination fields, or have fields/methods that are required in dog and are not present in cat. This types will trigger an error:
interface Dog extends Animal { eat(): void; bark(): void }
interface Cat extends Animal { eat(): void; scratch(): void; }
Upvotes: 1