6

Is there any way (special syntax) to apply something like rest parameters to templates in TypeScript?

If question is unclear then take a look at below example and comments.

Question is - can I apply here something like rest ...Types:

function mixIt<A, B>   (a: A, b: B): A & B;
function mixIt<A, B, C>(a: A, b: B, c: C): A & B & C;

/* 
 * ??
 * If I would like to add D, E, F, etc, do I have to overwrite it like above?
 */

function mixIt<A, B, C>(...args: any[]): any{
    let mixin = <A & B & C> {};

    args.forEach( obj => {
        for(let key in obj) {
            if( ! mixin.hasOwnProperty(key) ) {
                (<any>mixin)[key] = obj[key];
            }
        }
    });

    return mixin;
}

FYI - error detection is as expected:

class X {x: number = 7;}
class Y {y: string = 'ok';}
class Z {z: boolean = false;}

let x = new X;
let y = new Y;
let z = new Z;

let xy = mixIt(x, y);
let xyz = mixIt(x, y, z);

xyz.z; // Ok;
xy.z; // Error - as expected. VS Code editor also highlights it
1

1 Answer 1

9

Edit Since the original answer typescript has added support for tuples in rest parameters in 3.0. With this we can achieve the desired result without all the overloads:

type UnionToIntersection<U> = 
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

function mixIt<T extends any[]>(...args: T): UnionToIntersection<T[number]>{ 
    let mixin: any =  {};

    args.forEach( obj => {
        for(let key in obj) {
            if( ! mixin.hasOwnProperty(key) ) {
                mixin[key] = obj[key];
            }
        }
    });

    return mixin;
}

Original answer

There is no way to currently have a variable number of type parameters, there is a proposal on this.

The only way to currently do this is to add as many signatures to the functions as needed, so in your case this would be:

function mixIt<A, B>   (a: A, b: B): A & B;
function mixIt<A, B, C>(a: A, b: B, c: C): A & B & C;
function mixIt<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;
function mixIt<A, B, C, D, E>(a: A, b: B, c: C, d: D, e: E): A & B & C & D & E;
function mixIt<A, B, C, D, E, F>(a: A, b: B, c: C, d: D, e: E, f: F): A & B & C & D & E &F ;
// Private signature
function mixIt(...args: any[]): any{ // no need for this to be generic
    let mixin: any =  {};

    args.forEach( obj => {
        for(let key in obj) {
            if( ! mixin.hasOwnProperty(key) ) {
                mixin[key] = obj[key];
            }
        }
    });

    return mixin;
}
Sign up to request clarification or add additional context in comments.

4 Comments

You left in the comment "no need for this to be generic" on the now-generic function definition. But, hey, neat trick!
@Coderer wops, removed it. Saw it got upvoted and the info was a bit stale, wanted to do a quick update and forgot the comment :)
Wow, UnionToIntersection works but some magic happens there... Could you please explain why and how does it work? At first look U is inferred as I so U should be equal to I, but practically it isn't so. Also without U extends any check everything won't work, but can U not extend any?
I've found a little more descriptive answer: stackoverflow.com/a/50375286/5598194. The key to understadting is "distributive conditional types": typescriptlang.org/docs/handbook/release-notes/…. U extends any is used to enable this language feature and split inference into few ones, each per the union member. As I is located on a contra-variant position (i. e. as (k: U) => void) extends (k: I) => void, then I extends U), the final result is an intersection of all candidate types (that is just how TS works).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.