Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
253 views
in Technique[技术] by (71.8m points)

javascript - Auto-skip properties not belonging to the type in TypeScript

Let's say I have the following type declaration:

declare type Point2D = { x: number, y: number }

I fetch some data from the server and get the following:

const response = { x: 1.2, y: 3.4, foreign1: 'value', foreign2: 'value' }

Is it possible to automatically ignore all properties that do not belong to my type? Like this:

const point: Point2D = response // Should skip all props except for 'x' and 'y'

The important thing is that the reponse can have any number of foreign properties, so I cannot use object destructuring with the rest operator.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Types aren't available at runtime.

In order to make things DRY, helper definition object can be used:

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);

Because definition object relies on inferred type, it has certain limitations, e.g. intersection or union types cannot be used (a value cannot be a number and a string at the same time).

Notice that this code doesn't contain checks that point has all properties from Point2D, so technically it's more like point: Partial<Point2D>. It also doesn't check that values have same types as in definition.

Both checks can be additionally provided for type safety at runtime.

Alternatively, Point2D can be converted to a class that takes care of omitting unnecessary properties on construction.

Properties should be listed explicitly:

class Point2D {
    x: number;
    y: number;

    constructor({ x, y }: Point2D) {
        this.x = x;
        this.y = y;
    }
}

Validation can be optionally added to class constructor for type safety at runtime.

A workaround for not listing properties explicitly would to combine a class with helper definition object to iterate over object properties. Declaration merging can be used to assert that Point2D class has all properties that are listed in 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();
            }
        }
    }
}

The important thing is that the reponse can have any number of foreign properties, so I cannot use object destructuring with the rest operator.

Object destructuring results in WET but type-safe (at compilation time) code and certainly can be used for this purpose, for example:

const point: Point2D = (({ x, y }) => ({ x, y }))(response as Point2D);

It doesn't need the ...rest of the properties because they are supposed to be discarded.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...