TS 在父方法中重用 this.constructor

问题描述 投票:0回答:1

我有一个基类

Message
和一个用于额外功能的子类
SpecializedMessaged

class Message {
    constructor(
        readonly content: string
    ) { }
    edit(replacement: string) {
        return new Message(replacement)
    }
}

class SpecializedMessage extends Message {
    extra() {}
}

由于

.edit()
逻辑对于
Message
和所有子类都是相同的,因此我在基类
Message
中实现一次以进行继承。不变性在我的用例中很重要,因此每次编辑都必须返回一个新实例。

但是,

        return new Message(replacement)

将导致子实例不期望地转换回基本类型

let message = new SpecializedMessage('hello')
message.extra() // works
message = message.edit('world') // message now instanceof `Message` instead of `SpecializedMessage`
message.extra() // fails, `Message` lacks `.extra()`

我尝试使用

this.constructor
来解决这个问题,以自动使用正在使用的任何类实例的构造函数

class Message {
    // ...
    edit(replacement: string) {
        return new this.constructor(replacement)
    }
}

但是 TS 警报

This expression is not constructable.
  Type 'Function' has no construct signatures.ts(2351)
(property) Object.constructor: Function

尽管代码可以在普通 JavaScript 中运行。

我可以通过转换

this.constructor
as
构造函数来消除错误

class Message {
    // ...
    edit(replacement: string) {
        const ctor = this.constructor as { new (content: string): Message}
        return new ctor(replacement)
    }
}

但想要一个不需要额外步骤的解决方案。

为什么 TS 拒绝我使用

this.constructor()
?我该如何解决?

我知道,只要子类可以具有唯一的构造函数签名,这种方法本质上就是不安全的,但从错误消息来看,这似乎不是 TS 所关心的。

javascript typescript constructor subclassing type-assertion
1个回答
0
投票


编辑/注释 ...由于一些@Bergi的评论

Bergi 提出的类型断言

this.constructor as typeof Message
原因是人们应该走的黄金道路

OP 的实现将略有改变......

class Message {
  constructor(
    readonly content: string
  ) {
  }
  edit(replacement: string) {
    return new (this.constructor as typeof Message)(replacement);
  }
}

class SpecializedMessage extends Message {
  extra() {}
}


... 原答案 ...

类似的实现 ...

class Message {
  constructor(
    readonly content: string,
    public messageType = new.target,
  ) {
  }
  edit(replacement: string) {
    return new this.messageType(replacement)
  }
}

class SpecializedMessage extends Message {
  extra() {}
}

...使用

new.target
不会引发任何编译器警告。

上面以香草风格提供的代码按照OP的预期工作,看起来像这样......

class Message {
  constructor(content, messageType = new.target) {
    this.content = content;
    this.messageType = messageType;
  }
  edit(replacement) {
    return new this.messageType(replacement);
  }
}
class SpecializedMessage extends Message {
  extra() {}
}
const defaultMsg = new Message("default message");
const specialMsg = new SpecializedMessage("special message");

console.log(
  specialMsg.edit(`yet another ${ specialMsg.content }`).content
);
console.log(
  specialMsg.edit(`yet another ${ specialMsg.content }`) instanceof Message
);
console.log(
  specialMsg.edit(`yet another ${ specialMsg.content }`) instanceof SpecializedMessage
);

console.log(
  defaultMsg.edit(`yet another ${ defaultMsg.content }`).content
);
console.log(
  defaultMsg.edit(`yet another ${ defaultMsg.content }`) instanceof Message
);
console.log(
  defaultMsg.edit(`yet another ${ defaultMsg.content }`) instanceof SpecializedMessage
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

© www.soinside.com 2019 - 2024. All rights reserved.