clem
clem

Reputation: 897

TypeScript: How to merge an object from another interface?

I have the following interfaces:

interface TxInterface {
    field1: string;
    field2: string;
    field3: number;
}

interface RxInterface {
    field1: string;
    field2: string;
    field3: number;
    field4: string;
    field5: string;
}

When I receive an object from server, it is typed with RxInterface. In my App, I continue to use this object when I update values for instance.

When I would like to update object in server, I shall send "TxInterface", that contains some fields of "RxInterface".

So, how can I easily to merge my object from RxInterface to TxInterface before send and take only the fields of TxInterface?

Upvotes: 1

Views: 862

Answers (2)

jcalz
jcalz

Reputation: 329953

There's no way to do this merely with the interface definitions, since such definitions are part of the type system which is erased by runtime. If you need something done at runtime, you need to write code which does it at runtime. In this case, here's a generic function which extracts just the properties of an object matching a list of keys:

function extract<T extends object, K extends keyof T>(
    obj: T,
    keys: K[]
): Pick<T, K> {
    const ret = {} as Pick<T, K>;
    keys.forEach(k => ret[k] = obj[k])
    return ret;
}

And for your specific use case, you can make a conversion function whose types involve RxInterface and TxInterface, but which specifies the particular keys you care about as values and not types:

const rxToTx = (rx: RxInterface): TxInterface => // type annotation here
    extract(rx, ["field1", "field2", "field3"]); // explicit key values here

And you can verify that it works:

const rx: RxInterface = {
    field1: "yes",
    field2: "yep",
    field3: 100,
    field4: "no",
    field5: "nope"
}

const tx = rxToTx(rx);

console.log(tx);
// {field1: "yes", field2: "yep", field3: 100}

Hope that helps; good luck!

Upvotes: 1

Zazaeil
Zazaeil

Reputation: 4104

All right. It is a good starting point to introduce binary operations also known as "reductions".

Consider generic function signature: T -> T -> T. What it does is takes two parameters of the same type T and somehow "collapses" them into a single resulting instance of the same type T. As a trivial example, take + on numbers or concatenation on the strings.

It turns out that such a binary operation is unbelievably common across various branches of math and founded on a much more reliable reasons then just a programming convenience. For example, a "semigroup" is essentially the same binary operation with few additional properties, I won't explain here.

So, don't ignore it. Rather design you code in such a way that you can leverage binary operations of the abovementioned (generic) signature. The question is: how? Mapping! Have you ever heard something about "map/reduce" approach? That's exactly what you are about to implement: firstly map a non-reducible instance into a reducible one and then apply one of reductions you have in order to compute final result.

P.S. In your case mapping since to be trivial: just specify explicitly generic params, since a wider interface is also satisfies narrower interface.

Upvotes: 0

Related Questions