仅键入 class 的子 classes
Type for only subclasses of a class
如果我有一个基础 class 和继承自该基础的多个子 class,例如:
class Message {
constructor(public text: string) {}
}
class MessageA extends Message {
msgType: string = 'A';
constructor(
public text: string,
public aData: string
) {
super(text);
}
}
class MessageB extends Message {
msgType: string = 'B';
constructor(
public text: string,
public bData: number
) {
super(text);
}
}
…是否可以创建一个只接受子 classes 的类型?
我想要类似的东西
type Msg = MessageA | MessageB;
…这样我就可以写
const show = (msg: Msg): void => {
switch (msg.msgType) {
case 'A':
return console.log(`${msg.text}: ${msg.aData}`);
case 'B':
return console.log(`${msg.text}: ${msg.bData}`);
}
}
但是,上面的代码给我这个错误:
Property 'aData' does not exist on type 'Msg'.
Property 'aData' does not exist on type 'MessageB'.
如果我将类型更改为以下,show
将再次起作用:
type Msg = ({msgType: 'A'} & MessageA)
| ({msgType: 'B'} & MessageB);
但是如果我用新的 MessageB
调用 show
...
show(new MessageB('hello', 300));
…我得到这个错误:
Argument of type 'MessageB' is not assignable to parameter of type 'Msg'.
Type 'MessageB' is not assignable to type '{ msgType: "B"; } & MessageB'.
Type 'MessageB' is not assignable to type '{ msgType: "B"; }'.
Types of property 'msgType' are incompatible.
Type 'string' is not assignable to type '"B"'.(2345)
为基 class 的子class创建类型的好方法是什么?
此处的目标是确保处理程序在访问任何字段(不在基础 class 中)之前检查传递给它的对象的类型,并确保 switch
在添加未来子 class 时是详尽无遗的。
看起来您希望 Msg
成为 discriminated union,msgType
作为 判别式 属性。
很遗憾,您将 MessageA
和 MessageB
上的 msgType
属性注释为 string
,并且 string
不是有效的判别式 属性 类型。判别式必须是只接受单个值的单位类型,例如 undefined
或 null
,或 string/numeric/boolean literal type.
而不是 string
,您希望它们分别是字符串文字类型 "A"
和 "B"
。您可以通过显式注释它们,或将它们标记为 readonly
,这将导致编译器为它们推断文字类型:
class MessageA extends Message {
readonly msgType = 'A';
// (property) MessageA.msgType: "A"
constructor(
public text: string,
public aData: string
) {
super(text);
}
}
class MessageB extends Message {
readonly msgType = 'B';
// (property) MessageB.msgType: "B"
constructor(
public text: string,
public bData: number
) {
super(text);
}
}
执行此操作后,show()
函数中的缩小将起作用:
const show = (msg: Msg): void => {
switch (msg.msgType) {
case 'A':
return console.log(`${msg.text}: ${msg.aData}`); // okay
case 'B':
return console.log(`${msg.text}: ${msg.bData}`); // okay
}
}
如果我有一个基础 class 和继承自该基础的多个子 class,例如:
class Message {
constructor(public text: string) {}
}
class MessageA extends Message {
msgType: string = 'A';
constructor(
public text: string,
public aData: string
) {
super(text);
}
}
class MessageB extends Message {
msgType: string = 'B';
constructor(
public text: string,
public bData: number
) {
super(text);
}
}
…是否可以创建一个只接受子 classes 的类型?
我想要类似的东西
type Msg = MessageA | MessageB;
…这样我就可以写
const show = (msg: Msg): void => {
switch (msg.msgType) {
case 'A':
return console.log(`${msg.text}: ${msg.aData}`);
case 'B':
return console.log(`${msg.text}: ${msg.bData}`);
}
}
但是,上面的代码给我这个错误:
Property 'aData' does not exist on type 'Msg'.
Property 'aData' does not exist on type 'MessageB'.
如果我将类型更改为以下,show
将再次起作用:
type Msg = ({msgType: 'A'} & MessageA)
| ({msgType: 'B'} & MessageB);
但是如果我用新的 MessageB
调用 show
...
show(new MessageB('hello', 300));
…我得到这个错误:
Argument of type 'MessageB' is not assignable to parameter of type 'Msg'.
Type 'MessageB' is not assignable to type '{ msgType: "B"; } & MessageB'.
Type 'MessageB' is not assignable to type '{ msgType: "B"; }'.
Types of property 'msgType' are incompatible.
Type 'string' is not assignable to type '"B"'.(2345)
为基 class 的子class创建类型的好方法是什么?
此处的目标是确保处理程序在访问任何字段(不在基础 class 中)之前检查传递给它的对象的类型,并确保 switch
在添加未来子 class 时是详尽无遗的。
看起来您希望 Msg
成为 discriminated union,msgType
作为 判别式 属性。
很遗憾,您将 MessageA
和 MessageB
上的 msgType
属性注释为 string
,并且 string
不是有效的判别式 属性 类型。判别式必须是只接受单个值的单位类型,例如 undefined
或 null
,或 string/numeric/boolean literal type.
而不是 string
,您希望它们分别是字符串文字类型 "A"
和 "B"
。您可以通过显式注释它们,或将它们标记为 readonly
,这将导致编译器为它们推断文字类型:
class MessageA extends Message {
readonly msgType = 'A';
// (property) MessageA.msgType: "A"
constructor(
public text: string,
public aData: string
) {
super(text);
}
}
class MessageB extends Message {
readonly msgType = 'B';
// (property) MessageB.msgType: "B"
constructor(
public text: string,
public bData: number
) {
super(text);
}
}
执行此操作后,show()
函数中的缩小将起作用:
const show = (msg: Msg): void => {
switch (msg.msgType) {
case 'A':
return console.log(`${msg.text}: ${msg.aData}`); // okay
case 'B':
return console.log(`${msg.text}: ${msg.bData}`); // okay
}
}