TypeScript中的另一个多态性问题

TypeScript中的另一个多态性问题,typescript,class,polymorphism,Typescript,Class,Polymorphism,我有一个基本类: import { mapKeys } from 'lodash' export function initModel<T>(this: T, data: Partial<T>): void { for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { this[key] = data[key] } } } exp

我有一个基本类:

import { mapKeys } from 'lodash'

export function initModel<T>(this: T, data: Partial<T>): void {
  for (const key in data) {
    if (Object.prototype.hasOwnProperty.call(data, key)) {
      this[key] = data[key]
    }
  }
}

export class BaseModel {
  static mapFields: Record<string, string> = {}

  constructor(data?: Partial<BaseModel>) {
    initModel.call(this, data)
  }

  static parse<RawT, T extends BaseModel>(rawData: RawT): T {
    return Object.prototype.constructor.call(
      this,
      mapKeys(rawData, (_, k) => this.mapFields[k]),
    )
  }
}
TypeScript向我显示了重写解析方法时出现的错误:

export class RawEmployee {
  id: number
  image: string
  position: string
  department: string
  name: string
  last_event: string
}
TS2417:类静态端“typeof Employee”错误地扩展了基类静态端“typeof BaseModel”。属性“parse”的类型不兼容。类型'(rawData:RawEmployee)=>Employee'不可分配给类型'(rawData:RawT)=>T'。参数“rawData”和“rawData”的类型不兼容。类型“RawT”不可分配给类型“RawEmployee”


如何正确实现它?

以下代码不起作用

[编辑] 此代码不再引发任何错误

"typescript": "^4.0.2"
"lodash": "^4.17.20",
"@types/lodash": "^4.14.161",
但是,请注意以下几点:

1-在
initModel
函数中,当设置
此[键]
时,通过添加空断言(请注意
)可以绕过某些错误。我了解到,如果类扩展BaseModel的部分键传入构造函数,那么您就是在设置它们。似乎Typescript不喜欢这里的
部分。由于我不理解testif Object.prototype.**,我不确定是否理解这个
initModel
函数的真正目标。可能还有另一种编写方法来避免糟糕的

2-根据lodash mapKeys函数的类型定义,需要将对象作为参数传递。因此,您的
rawT
类型应该扩展
Record
(或Record

从“lodash”导入{mapKeys};
导出函数初始化模型(
这:T,,
数据:部分
):无效{
for(常量输入数据){
if(Object.prototype.hasOwnProperty.call(数据、键)){
此[键]=数据[键]!;
}
}
}
导出类基模型{
静态映射字段:记录={};
构造函数(数据?:部分){
data&&initModel.call(this,data);
}
静态解析(
rawData:RawT
):T{
返回Object.prototype.constructor.call(
这
mapKeys(rawData,(u:any,k:any)=>this.mapFields[k])
);
}
}
导出类Employee扩展BaseModel{
id:编号;
图像:字符串;
位置:字符串;
部门:字符串;
名称:字符串;
最后事件:日期;
静态映射字段={
id:“id”,
图像:“图像”,
职位:“职位”,
部门:“部门”,
姓名:“姓名”,
最后事件:“最后事件”,
};
静态解析(rawData:RawEmployee):Employee{
const result=super.parsing(rawData);
result.lastEvent=新日期(result.lastEvent);
返回结果;
}
}
出口类员工{
id:编号;
图像:字符串;
位置:字符串;
部门:字符串;
名称:字符串;
最后一个事件:字符串;
}

目前,我的解决方案是:

export class BaseModel {
  static mapFields: Record<string, string> = {}

  constructor(data?: Partial<BaseModel>) {
    initModel.call(this, data)
  }

  static parse(rawData: Record<string, unknown>): BaseModel {
    return Object.prototype.constructor.call(
      this,
      mapKeys(rawData, (_, k) => this.mapFields[k]),
    )
  }
}

export class RawEmployee implements Record<string, unknown> {
  [x: string]: unknown
  id: number
  image: string
  position: string
  department: string
  name: string
  last_event: string
}

export class Employee extends BaseModel {
  id: number
  image: string
  position: string
  department: string
  name: string
  lastEvent: Date

  static mapFields = {
    id: 'id',
    image: 'image',
    position: 'position',
    department: 'department',
    name: 'name',
    last_event: 'lastEvent',
  }

  static parse(rawData: RawEmployee): Employee {
    const result = super.parse(rawData) as Employee
    result.lastEvent = new Date(result.lastEvent)
    return result
  }
}
导出类基模型{
静态映射字段:记录={}
构造函数(数据?:部分){
initModel.call(这个,数据)
}
静态解析(rawData:Record):BaseModel{
返回Object.prototype.constructor.call(
这
mapKeys(rawData,(u,k)=>this.mapFields[k]),
)
}
}
导出类RawEmployee实现记录{
[x:字符串]:未知
身份证号码
图像:字符串
位置:字符串
部门:字符串
名称:string
最后一个事件:字符串
}
导出类Employee扩展BaseModel{
身份证号码
图像:字符串
位置:字符串
部门:字符串
名称:string
最后事件:日期
静态映射字段={
id:'id',
图像:“图像”,
位置:'位置',
部门:'部门',
姓名:'姓名',
上次事件:“上次事件”,
}
静态解析(rawData:RawEmployee):Employee{
const result=super.parse(rawData)作为雇员
result.lastEvent=新日期(result.lastEvent)
返回结果
}
}

它并不完美,因为我需要覆盖所有模型中的parse方法,即使它与原始模型没有区别。并且键入guard
,因为超级调用需要

什么是
RawEmployee
?@gurisko我添加了它。你找到解决方法了吗?它看起来像是TS中的bug…@Jerome是的,我可以创建一个作为cop的中间件类BaseModel的y,但没有解析方法,然后扩展Employee,并将super调用切换到BaseModel调用。作为一种解决方法,它可以很好地工作。我仍在研究中。玩你的代码时,我发现如果我通过
解析
或任何其他方式更改你的
解析
静态方法的名称,eslint警告就会消失……不要担心现在,如果这个静态模型中存在冲突,namingI在问题中添加了mapKeys和initModel函数。
import { mapKeys } from "lodash";

export function initModel<T>(
    this: T,
    data: Partial<T>
): void {
    for (const key in data) {
        if (Object.prototype.hasOwnProperty.call(data, key)) {
            this[key] = data[key]!;
        }
    }
}

export class BaseModel {
    static mapFields: Record<string, string> = {};

    constructor(data?: Partial<BaseModel>) {
        data && initModel.call(this, data);
    }

    static parsing<RawT extends Record<string, any>, T extends BaseModel>(
        rawData: RawT
    ): T {
        return Object.prototype.constructor.call(
            this,
            mapKeys(rawData, (_: any, k: any) => this.mapFields[k])
        );
    }
}

export class Employee extends BaseModel {
    id: number;
    image: string;
    position: string;
    department: string;
    name: string;
    lastEvent: Date;

    static mapFields = {
        id: "id",
        image: "image",
        position: "position",
        department: "department",
        name: "name",
        last_event: "lastEvent",
    };

    static parse(rawData: RawEmployee): Employee {
        const result = super.parsing<RawEmployee, Employee>(rawData);
        result.lastEvent = new Date(result.lastEvent);
        return result;
    }
}

export class RawEmployee {
    id: number;
    image: string;
    position: string;
    department: string;
    name: string;
    last_event: string;
}
export class BaseModel {
  static mapFields: Record<string, string> = {}

  constructor(data?: Partial<BaseModel>) {
    initModel.call(this, data)
  }

  static parse(rawData: Record<string, unknown>): BaseModel {
    return Object.prototype.constructor.call(
      this,
      mapKeys(rawData, (_, k) => this.mapFields[k]),
    )
  }
}

export class RawEmployee implements Record<string, unknown> {
  [x: string]: unknown
  id: number
  image: string
  position: string
  department: string
  name: string
  last_event: string
}

export class Employee extends BaseModel {
  id: number
  image: string
  position: string
  department: string
  name: string
  lastEvent: Date

  static mapFields = {
    id: 'id',
    image: 'image',
    position: 'position',
    department: 'department',
    name: 'name',
    last_event: 'lastEvent',
  }

  static parse(rawData: RawEmployee): Employee {
    const result = super.parse(rawData) as Employee
    result.lastEvent = new Date(result.lastEvent)
    return result
  }
}