我正在开发一个用于复杂api模型的实用程序库。我们从wire接收json解析对象,并对其结构进行某种保证,如下所示:
// Known structure of the message:
interface InputBox {
top: number;
height: number;
left: number;
width: number;
}
interface InputObj {
box: InputBox
}
// User code (outside our scope) does something like:
const inputObj: InputObj = JSON.parse(
'{ "box": { "top": 0, "height": 10, "left": 1, "width": 2, "color": "red" } }'
);
我的目标是创建对象的一些视图,其中:
添加一些实用程序函数和属性
字符串化为与输入相同/兼容的json
当被视为可变时,行为一致
如果可能,保留任何意外属性
例如,用户代码可能如下所示:
// In-place or new obj constructor would both be fine:
const easyObj = myCoolLibrary(inputObj);
easyObj.box.top = 5;
console.log(easyObj.box.getBottom()); // '15'
JSON.stringify(easyObj);
// -> { "box": { "top": 5, "height": 10, "left": 1, "width": 2, "color": "red" } }
// (Note box.color still present, although missing from the input interface)
在阅读了一些关于选项的内容后,似乎:
使用object.setprototypeof直接覆盖输入中的原型可能有一个很好的就地解决方案,但这是最近的一个解决方案,不确定polyfilling是否适用于较旧的浏览器。
创建新对象(使用类/原型)并跨object.assign克隆属性可能会起作用,因为已知输入对象是简单/可复制的 Object
Sie上仍然需要polyfill,但可能更标准一点?
问题是,即使这些方法是可行的,我也看不出它们是如何以一种让typescript满意的方式实现的?
例如:
class MyCoolBox implements InputBox {
constructor(box: InputBox) {
Object.assign(this, box);
}
getBottom() {
return this.top + this.height;
}
}
// > Class 'MyCoolBox' incorrectly implements interface 'InputBox'.
// (and doesn't recognise presence of .top, .height)
Object.setPrototypeOf(inputObj.box, MyCoolBox);
inputObj.box.getBottom();
// > Property 'getBottom' does not exist on type 'InputBox'
// Doesn't recognise the change of interface.
我是否错过了typescript可以理解的合理方法?这似乎是一个合理的要求,只是装饰一个json解析对象(已知接口)与一些方法!
1条答案
按热度按时间qhhrdooz1#
首先,你似乎对如何做有误解
implements
作品属性是可识别的,只是您告诉编译器您的类实现了这些属性,但从未实际实现它们。是的,你做这个Object.assign
在构造函数中,它在运行时实现了所需的结果,但编译器不知道这一点。因为您显然不想详细说明
InputBox
在类上,解决方案是使用这样一个事实,即类具有绑定到它们的同名接口。因此,您可以使MyCoolBox
接口是要扩展的接口的子类型(即InputBox
):其次,你期望
setPrototypeOf
其行为类似于某种类型的guard,但其定义如下:也就是说,通过更改原型,编译器对原型形状的了解不会发生任何变化
box
对象:对于它来说,它仍然是一个InputBox
. 还有一个问题是class
javascript中的es主要是函数+基于原型的继承的语法糖。设置
box
原型MyCoolBox
当您尝试调用getBottom
方法,因为它不是静态的,并且您仅以这种方式设置类的静态端。您实际上想要的是设置MyCoolBox
-这将设置示例属性:最后,根据上面的示例,您需要告诉编译器输出类型是增强类型。您可以通过一个简单的类型Assert来实现这一点(
as T & { box: MyCoolBox }
)如上所述,或者让编译器为您推断增强类型:操场