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