1

I'm encountering a scenario where I have many functions with similar parameters and struggling to find a solution to make everything easily maintainable. Take the following example:

public func1(param1: string, param2: string, param3: string){}
public func2(param1: string, param2: string, param3: string){}
public func3(param1: string, param2: string, param3: string){}

public func4(param1: string, param2: string, param3: string, param4: string){}
public func5(param1: string, param2: string, param3: string, param4: string){}
public func6(param1: string, param2: string, param3: string, param4: string){}

... Etc

Initially I was thinking about an interface such as:

interface IMyInterface {
  param1: string;
  param2: string;
  param3: string;
}

and using it in the fuction:

public func1(args: IMyInaterface) {

}

But then in fuction 4,5,6 I can't really extend it to accept N number of additional args without creating new interfaces and I don't want to maintain all the derived interfaces.

I'd also like to keep my typings (not all parameters are strings and all functions have a defined number of parameters) so something like

func1(...args: string){}

wouldn't work

Any Idea how best to go about this?

4
  • One plain object with 4 properties to start? Then you can add as many properties later as you want. Commented Sep 27, 2019 at 13:00
  • is implementation of your functions the same? Commented Sep 27, 2019 at 13:34
  • If my answer doesn't meet your neds then please expand your code to a minimal reproducible example with more detailed use cases. When you say "extend to accept N number of additional args without creating new interfaces" you might want to be more explicit, since it's trivial to prepend arguments (func(newArg: boolean, param1: string, param2: string, param3: string){}) but clunky/messy to append arguments (func(param1: string, param2: string, param3: string, newArg: boolean){}), and I'm not sure if N is something you need to be generic or what. Good luck! Commented Sep 27, 2019 at 13:44
  • what about func1(...args: any[]){}? or func1(one:string,two:number,...rest:any[]){}, see typescriptlang.org/docs/handbook/functions.html#rest-parameters Commented Sep 28, 2019 at 12:07

4 Answers 4

3

in function 4,5,6 I can't really extend it to accept N number of additional args without creating new interfaces

Actually, you could use an intersection type to extend an interface "inline":

interface BaseArgs {
    foo:string, 
    bar: number
}

function func1(a: BaseArgs) {}
function func2(a: BaseArgs & {more: boolean}) {}

func1({foo: "aa", bar: 3}) // ok
func2({foo: "aa", bar:3, more: true}) // ok
Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly what I was looking for, thank you! I just couldn't figure out the syntax for something like that.
1

You can use the question mark "?" to represent if a variable is necessary or not, for instance:

public func4(param1?: string, param2?: string, param3?: string, param4?: string){}

In this function you can pass 0, 1, 2, 3 or 4 params.

1 Comment

All parameters on all functions are necessary and they need to be constrained to just those parameters to get the proper typing and ease for refactoring
1

Since TypeScript 3.0 we've been able to represent function parameter lists as tuple types. So your use case can be supported by something like this:

type ThreeStrings = [string, string, string];
type FourStrings = [string, string, string, string];

class Blarg {
  public func1(...param: ThreeStrings) {}
  public func2(...param: ThreeStrings) {}
  public func3(...param: ThreeStrings) {}
  //(method) Blarg.func3(param_0: string, param_1: string, param_2: string): void

  public func4(...param: FourStrings) {}
  public func5(...param: FourStrings) {}
  public func6(...param: FourStrings) {}
  // (method) Blarg.func6(param_0: string, param_1: string, param_2: string, param_3: string): void
}

You can see that IntelliSense shows the methods as having distinct parameters like param_0, param_1, etc.


You can also have IntelliSense keep track of parameter names by using the following notation:

type OtherParams = Parameters<
  (name: string, age: number, likesAsparagus: boolean) => void
>;


class Blarg {
  public func7(...param: OtherParams) {}
  public func8(...param: OtherParams) {}
  public func9(...param: OtherParams) {}
  // (method) Blarg.func9(name: string, age: number, likesAsparagus: boolean): void
}

Okay, hope that helps. Good luck!

Link to code

3 Comments

Maintaining the list of tuples would be about the same as a list of interfaces since each tuple is unique instead of inheriting from something like a base tuple.
Please post a minimal reproducible example which demonstrates what a solution you'd accept would look like. Obviously you don't have an implementation or you wouldn't ask the question, but you're presumably thinking of a general form of solution that would be acceptable. e.g., "if I could do public func515(...param: MagicStuff<ThreeStrings, SomethingElse>){} which had the effect of ______ and ______, that would be ideal."
never mind, I see you wanted to intersect interfaces. Cheers!
0

Alright, so far as I can see you have two options.

The first one: you can create an interface with all the parameters that the functions must have as a base and then pass an object with the additional attributes. i.e:

interface BaseParams {
  param1: string
  param2: string
}

const params = {
  param1 = 'the 3rd'
  param2 = 'is'
  param3 = 'additional'
}

The key here, is to not add the params object to be part of BaseParams, then you can create the function like this:

const funct = (params: BaseParams) => {
...
}

//call it with the object you created
funct(params)

Typescript will only evaluate if the object you are passing has the fields from the interface you created.

The other option and the less known is to use Partial from Typescript, it makes all the attributes you declared in the interface to be optional, so you won't have to pass each param like param1?: string. In this order, you have to create an interface with all the possible params.

interface Params {
 param1: string
 param2: string
 param3: string
 param4: string
 paramN: string
}

const funct = (params: Partial<Params>) => {
...
}

1 Comment

This was my line of thought as well but neither are a solution that I found to be better than just passing in the parameters by themselves. In the first option param3 would not be known inside funct and would cause an error if you tried to access it. The second option would not cause an error if I did not pass in all the parameters I need to the interface.

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.