假设我有以下类型声明:
declare type Point2D = { x: number, y: number }
我从服务器获取一些数据并获取以下内容:
const response = { x: 1.2, y: 3.4, foreign1: 'value', foreign2: 'value' }
是否可以自动忽略所有不属于我的类型的属性?像这样:
const point: Point2D = response // Should skip all props except for 'x' and 'y'
重要的是响应可以包含任意数量的外来属性,因此我不能将对象解构与其余运算符一起使用。
答案 0 :(得分:5)
类型在运行时无法使用。
为了使DRY成为可能,可以使用辅助定义对象:
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();
}
}
}
}
重要的是响应可以包含任意数量的外来属性,因此我不能将对象解构与其余运算符一起使用。
对象解构导致WET但是类型安全(在编译时)代码,当然可以用于此目的,例如:
const point: Point2D = (({ x, y }) => ({ x, y }))(response as Point2D);
它不需要...rest
属性,因为它们应该被丢弃。