Typescript: Mapped Types: Class to Interface without methods
It's possible to define the IDog type without methods, as described in TS documentation. But unfortunately it still contains dogYears getter. As I know, it cannot be solved because when dealing with types, there is no difference between fields and getters.
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
type IDog = NonFunctionProperties<Dog>;
Interface Based on Class
You can directly generate an interface from a class in TypeScript:
interface DogLike extends Dog {
}
The Angular community is all over this, but beware of some warnings about using classes as interfaces.
The interface that this would generate for you would include properties and methods:
interface DogLike {
name: string;
age: number;
bark(): void;
dogYears: number;
}
Mapped Type Madness
Now, you can do something really clever/complex/mad with mapped types, based on the mapped types found in this article.
type Remove<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];
type RemoveProperty<T, K extends keyof T> = { [P in Remove<keyof T, K>]: T[P] };
type PartDog = RemoveProperty<DogLike, 'bark' | 'dogYears'>;
This takes the DogLike
interface, and removes bark
and dogYears
.
I included this as you mentioned mapped types. However, it is an insane solution.
Interface
My recommended solution, would be a a simple interface, and perhaps one that isn't named after dogs at all, as the properties are more general:
interface NamedLivingOrganism {
name: string;
age: number;
}
Okay, you may not name it exactly like that. However, simple is often best and when you come to change either the Dog
class, or the interface that is loosely based on it at some point in the future, the simple interface will prove to be the best way to represent what you need.