是否可以编写将类转换为接口的映射类型减去类的方法或分配给prototype
的任何属性。 EG,
class Dog {
name: string;
age: number;
bark() {}
get dogYears(): number {
return this.age * 7;
}
}
type InterfaceOf<T> = { /* ??? */ };
type IDog = InterfaceOf<Dog>;
// equivalent to
interface IDog {
name: string;
age: number;
}
我为什么要这样做? 我希望将json对象“反序列化”为类。例如,我运行一个查询来从数据库中获取狗,之后我想将它们实例化为类对象,可能是使用class-transformer库。
function queryDogs(): Promise<{ name: string, age: string }[]>;
function deserialize<T>(cls: (new() => T), input: InterfaceOf<T>): T;
function deserialize<T>(cls: (new() => T), inputs: InterfaceOf<T>[]): T[];
class Dog {
@Type(() => String)
name: string;
@Type(() => Number)
age: number;
bark() {}
get dogYears(): number {
return this.age * 7;
}
}
const dbDogs = await queryDogs();
const dogs: Dog[] = deserialize(Dog, dogs);
如果deserialize
函数知道输入是否是要反序列化为Dog类的正确形状,那将是很好的。我希望它可以查看给它的Dog
类,将其转换为适当的接口。
答案 0 :(得分:10)
您可以直接从TypeScript中的类生成接口:
interface DogLike extends Dog {
}
Angular社区就是这样,但要注意some warnings about using classes as interfaces。
这将为您生成的接口包括属性和方法:
interface DogLike {
name: string;
age: number;
bark(): void;
dogYears: number;
}
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'>;
这会占用DogLike
界面,并移除bark
和dogYears
。
我在你提到的映射类型中包含了这个。但是,这是一个疯狂的解决方案。
我推荐的解决方案是一个简单的界面,也许是一个完全不以狗命名的界面,因为属性更通用:
interface NamedLivingOrganism {
name: string;
age: number;
}
好的,你可能不会那样命名。然而,简单通常是最好的,当你来改变Dog
类,或者在将来的某个时候松散地基于它的接口时,简单的接口将被证明是表示什么的最佳方式你需要。
答案 1 :(得分:1)
可以将没有方法的IDog类型定义为described in TS documentation。但不幸的是,它仍然包含dogYears getter。据我所知,这是无法解决的,因为在处理类型时,字段和获取方法之间没有区别。
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>;