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
fider
  • 1,976
  • 26
  • 29
  • https://github.com/Microsoft/TypeScript/issues/5453#issuecomment-646373551 `OR` https://github.com/Microsoft/TypeScript/issues/5453#issuecomment-646365222 -- The examples given in these two links seems to do the trick. – Jarrett Sep 27 '21 at 01:37

1 Answers1

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;
}
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • You left in the comment "no need for this to be generic" on the now-generic function definition. But, hey, neat trick! – Coderer Jan 08 '19 at 08:20
  • 1
    @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 :) – Titian Cernicova-Dragomir Jan 08 '19 at 08:22
  • 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`? – N. Kudryavtsev Jun 30 '21 at 13:49
  • I've found a little more descriptive answer: https://stackoverflow.com/a/50375286/5598194. The key to understadting is "distributive conditional types": https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types. `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). – N. Kudryavtsev Jun 30 '21 at 15:11