类型在运行时不可用。
为了让事情变得干燥,可以使用辅助定义对象:
const Point2DDefinition = { x: 1, y: 1 };
type Point2D = typeof Point2DDefinition;
const point: Point2D = Object.entries(response)
.filter(([k]) => Object.keys(Point2DDefinition).includes(k))
.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as Point2D);
由于定义对象依赖于推断类型,它有一定的局限性,例如不能使用交集或联合类型(值不能同时是数字和字符串)。
请注意,此代码不包含检查 point 是否具有来自 Point2D 的所有属性,因此从技术上讲,它更像 point: Partial<Point2D>。它也不检查值是否与定义中的类型相同。
可以额外提供这两种检查以确保运行时的类型安全。
或者,Point2D 可以转换为一个类,该类负责在构造时省略不必要的属性。
应明确列出属性:
class Point2D {
x: number;
y: number;
constructor({ x, y }: Point2D) {
this.x = x;
this.y = y;
}
}
可以选择将验证添加到类构造函数中,以确保运行时的类型安全。
不显式列出属性的解决方法是将类与辅助定义对象结合起来迭代对象属性。 Declaration merging 可用于断言Point2D 类具有Point2DDefinition 中列出的所有属性:
type TPoint2D = typeof Point2DDefinition;
interface Point2D extends TPoint2D {};
class Point2D {
constructor(point: Point2D) {
for (const k of Object.keys(Point2DDefinition)) {
// runtime check for value types can also be added
if (k in point) {
this[k] = point[k];
} else {
throw new TypeError();
}
}
}
}
重要的是响应可以有任意数量的外来属性,所以我不能将对象解构与 rest 运算符一起使用。
对象解构会产生 WET 但类型安全(在编译时)的代码,当然可以用于此目的,例如:
const point: Point2D = (({ x, y }) => ({ x, y }))(response as Point2D);
它不需要属性的...rest,因为它们应该被丢弃。