打字稿:映射类型:没有方法的类到接口

时间:2017-12-19 18:28:06

标签: typescript typescript-2.5

是否可以编写将类转换为接口的映射类型减去类的方法或分配给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类,将其转换为适当的接口。

2 个答案:

答案 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;
}

映射类型疯狂

现在,你可以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'>;

这会占用DogLike界面,并移除barkdogYears

我在你提到的映射类型中包含了这个。但是,这是一个疯狂的解决方案。

接口

我推荐的解决方案是一个简单的界面,也许是一个完全不以狗命名的界面,因为属性更通用:

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>;