Jukka Varis Jukka Varis - 2 months ago 24
TypeScript Question

How to extend anonymous type in Typescript

When chaining functionality in

typescript
with anonymous types for example like this:

let array = [{ seed: 2 }, { seed: 3 }];
array
.map(i => ({ seed: i.seed, square: i.seed * i.seed }))
.forEach(i => console.log(`square for ${i.seed} is ${i.square}`));


I need to define new anonymous type for map function. If I would have multiple steps all producing new properties, I'd end up writing lots of definition code to get all properties carried over.
I could use
$.extend
(or
Object.assign
), but that way I'll lose intellisense and strong typing.

array
.map(i => $.extend(i, { square: i.seed * i.seed }))
.forEach(i => console.log(`square for ${i.seed} is ${i.square}`));


How can I extend anonymous object without defining all properties again while keeping strong typing?

Answer

I have finally found a solution. This can be achieved by Intersection Types. Those can be used with anonymous types and classes. In the example below, extend function will copy properties from each of objects and return an object of Intersection Type. This will reduce considerable amount of type definition code without losing intellisense and strong typing.

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}

let array = [{ seed: 2 }, { seed: 3 }];

array
    .map(i => extend(i, { square: i.seed * i.seed }))
    .map(i => extend(i, { cube: i.square * i.seed }))
    .forEach(i => console.log(`square for ${i.seed} is ${i.square} and cube is ${i.cube}`));

Same in Playground

This is implemented f.e. in core-js and its type definitions return Intersection Type:

assign<T, U>(target: T, source: U): T & U;