Ruan Mendes
Ruan Mendes

Reputation: 92274

How to overload a function with variable arguments

I currently have a translation pipe that takes variable arguments

@Pipe({name: 'translate', pure: true})
export class TranslationPipe {
    constructor(private service: TranslationService) {}
    transform(key: string, ...params: (number|string)[]): string {
        // The translation service takes an array
        return this._service.translate(key, params);
    }
}

For a message like error.message=You have {0} of {1} tries left, it's called like the following in a template

{{ 'error.message' | translate : currentTry : totalTriesAllowed }}

However, we now have a case where a component takes the key and the replacements parameters as an array (because it doesn't know how many there could be since the key is provided by the caller) and I don't know of a good way to create an overload for that method.

I did try to spread the arguments from the template but that is not valid syntax

{{ translationKey | translate : ...translationArgs }}

To be able to make this work, I thought I'd need an overload like the following so that the translation pipe can also pass an array as the first argument instead of separate arguments

transform(key: string, params: (string|number)[]): string;
transform(key: string, ...params: (string|number)[]): string;

However, I haven't been able to find a good implementation for supporting this overload. This is where I am currently.

type ArryayOfStringOrNumber  = (string|number)[];
export class TranslationPipe {
    // ERROR: Overload signature is not compatible with function implementation
    transform(key: string, params: ArryayOfStringOrNumber): string;
    transform(key: string, ...params: ArryayOfStringOrNumber): string;
    transform(key: string, ...params: ArryayOfStringOrNumber): any {
        if (params[0] instanceof Array) {
            // ERROR: The left-hand side of an 'instanceof' expression must 
            //        be of type 'any', an object type or a type parameter.
            params = params[0] as ArryayOfStringOrNumber;
        }
        return this._service.translate(key, params as ArryayOfStringOrNumber);
    }
}

Upvotes: 2

Views: 2646

Answers (2)

Tao
Tao

Reputation: 2242

I just had a similar problem and what I did was split the first possible argument of the spread array, make it a union type and prepend it to the spread array if necessary.

The result was kinda like this:

foo(bar: Bar): void;
foo(...bazs: Array<Baz>): void;
foo(barOrBaz: Bar | Baz, ...bazsRest: Array<Baz>): void {
    if (barOrBaz instanceof Bar) {
        let bar = barOrBaz;
        // first overload
    } else {
        let bazs = [barOrBaz, ...bazsRest];
        // second overload
    }
}

So in your case the implementation could also be written like this:

transform(key: string, paramsFirst: string | number | ArrayOfStringOrNumber, ...paramsRest: ArrayOfStringOrNumber): string {
    let params = paramsFirst instanceof Array ? paramsFirst : [paramsFirst, ...paramsRest];
    return this._service.translate(key, params);
}

Upvotes: 2

Ruan Mendes
Ruan Mendes

Reputation: 92274

The problem is that the function implementing the overload has to accept an array of objects where each could be of type (string|number)[] or an array of those, that is ((string|number)[])[]

transform(key: string, ...params: (ArryayOfStringOrNumber|(string|number))[]): any {
    if (params[0] instanceof Array) {
        params = params[0] as ArryayOfStringOrNumber;
    }
    return this._service.translate(key, params as ArryayOfStringOrNumber);
}

Upvotes: 1

Related Questions