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

spa*_*tes 2 typescript typescript-2.5

是否可以编写将类转换为接口的映射类型减去类的方法或分配给它的任何属性prototype.例如,

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;
}
Run Code Online (Sandbox Code Playgroud)

我为什么要这样做? 我希望将json对象"反序列化"为类.例如,我运行一个查询来从数据库中获取狗,之后我想将它们实例化为类对象,也许是通过使用类变换器库.

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);
Run Code Online (Sandbox Code Playgroud)

如果deserialize函数知道输入是否是要反序列化为Dog类的正确形状,那将是很好的.我希望它可以查看Dog给它的类,将其转换为适当的接口.

Fen*_*ton 10

基于类的接口

您可以直接从TypeScript中的类生成接口:

interface DogLike extends Dog {

}
Run Code Online (Sandbox Code Playgroud)

Angular社区就是这样,但要注意关于使用类作为接口一些警告.

这将为您生成的接口将包括属性和方法:

interface DogLike {
    name: string;
    age: number;
    bark(): void;
    dogYears: number;
}
Run Code Online (Sandbox Code Playgroud)

映射型疯狂

现在,您可以根据本文中找到的映射类型,使用映射类型做一些非常聪明/复杂/疯狂的事情.

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'>;
Run Code Online (Sandbox Code Playgroud)

这需要DogLike接口,并删除barkdogYears.

我提到映射类型时包含了这个.然而,这是一个疯狂的解决方案.

接口

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

interface NamedLivingOrganism {
    name: string;
    age: number;
}
Run Code Online (Sandbox Code Playgroud)

好吧,你可能不会那样命名.然而,简单的往往是最好的,当你到了改变无论是Dog类,或者是松散的基础上它在未来的某个时刻的界面,简单的界面将被证明是代表你需要什么是最好的方式.


Ale*_*rov 6

可以在没有方法的情况下定义 IDog 类型,如TS 文档中所述。但不幸的是它仍然包含 dogYears getter。据我所知,它无法解决,因为在处理类型时,字段和 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>;
Run Code Online (Sandbox Code Playgroud)