6

Edit Sorry my question was a bit unclear. I want to enforce that the getList Parameter is always required. So I don't have a default value for it. e.g I want the user always to supply a getlist

I'm trying to create a constuctor with some optional parameters and some required

export class PageConfig {
    constructor({
        isSliding = false,
    }: {
        isSliding?: boolean
        getList: (pagingInfo: PagingInfo) => Observable<any>
    } = {  }) { }
}

When I do this I'm getting an error

getList is missing in type '{}' but required in type ...

I would like to be able to use this in a class like so:

class UsersPage {

    config = new pageConfig({ this.getList })    

    getList(pagingInfo: PagingInfo) {
      // do work...
    }
}

Note: I've simplified the class for this example but I have a lot more properties, I'd like to leverage desturcturing so I don't have to configure the instantiation of my classes other than from the declaration

How can I enforce that getList must be passed during destructuring?

johnny 5
  • 19,893
  • 50
  • 121
  • 195
  • I think you’ll want to define an interface of arguments for your constructor. – westandy Aug 23 '19 at 23:37
  • config = new pageConfig({ ...this.getList }) – chrismclarke Aug 23 '19 at 23:39
  • Well, you'll also need to add a default value for `getList` if the property is required – MinusFour Aug 23 '19 at 23:39
  • 1
    I'm confused. Where do you want to use destructuring? In the `PageConfig` constructor implementation signature? Or inside *calls* to the `PageConfig` constructor? If the former, it's going to be redundant like `constructor({ isSliding = false, getList }: { isSliding?: boolean; getList: GetListFunc;})`. If the latter, `new PageConfig({this.getList})` isn't valid syntax; you might need to do `new PageConfig(this)` or `new PageConfig({getList: this.getList})`. But I really don't understand the intended use case enough to make these into answers. – jcalz Aug 24 '19 at 00:28
  • 1
    @jcalz You cannot do destructuring in a call. – Bergi Aug 24 '19 at 11:17
  • 2
    Why did you declare `getList` as a property of your parameter, but then did not use it in the destructuring? – Bergi Aug 24 '19 at 11:19
  • @Bergi, I want to take in a getList, but I want it to be required I don't have an optional or default value for it – johnny 5 Aug 24 '19 at 21:39
  • @MinusFour the getList Property is Required, but I want to enforce that the user always has to add a getlist, So I don't want to provide a default value – johnny 5 Aug 24 '19 at 21:40
  • @johnny5 If you want to take it in, then you should declare it as a parameter (not just define it in the parameter's type). And if you want to have it always passed as an argument, you shouldn't provide an empty object as the default value for your parameter. – Bergi Aug 24 '19 at 22:00

3 Answers3

5

You use a default value {} for the PageConfig constructor argument, but you marked getList as required in the type. If I understand you correctly, you want to have the constructor argument and getList always set, so change your code to:

export class PageConfig {
  constructor({
    getList,
    isSliding = false
  }: {
    getList: (pagingInfo: PagingInfo) => Observable<any>;
    isSliding?: boolean;
  }) {
    … // use getList and isSliding
  }
}

This syntax (destructuring with default values) can be a bit cumbersome sometimes. It gets clearer, when you extract the constructor argument type.

TypeScript docs example

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
ford04
  • 66,267
  • 20
  • 199
  • 171
  • thanks for answering my question, I've created another related one on how to expose the constructor arguments as public properties of the class, If you have time it might be an opportunity to get some easy points since you seem to be familiar with this field. [The question is here](https://stackoverflow.com/q/57642204/1938988) – johnny 5 Aug 24 '19 at 23:42
3

You want to remove the default, and also change the parameter order - required parameters always come before optional parameters:

constructor(getList: (pagingInfo: PagingInfo) => Observable<any>, isSliding?: boolean = false) {...}
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
  • I got the impression he does want the default value – MinusFour Aug 23 '19 at 23:45
  • 5
    `required parameters always come before optional parameters` are you sure about that? The question (and your answer) uses object destrucuring on the first argument of the constructor. Object keys are unordered. Required vs optional is irrelevant in this situation. – Jake Holzinger Aug 23 '19 at 23:45
  • Fixed @JakeHolzinger and MinusFour. – Jack Bashford Aug 23 '19 at 23:52
  • 2
    This will work, but it changes the contract proposed in the question. However, it could be an acceptable alternative. – Jake Holzinger Aug 24 '19 at 00:04
  • @JackBashford, I was attempting to do it this way first, but, Imagine you have another optional parameter after isSliding, how can you supply that parameter, with out supplying isSliding? e.g I have 10 optional parameters, Is there a way to call the constructor while leveraging only some of the defaults? – johnny 5 Aug 24 '19 at 21:45
  • No, because unlike Swift we don't have named parameters. I think if you want that, an object is the best approach. – Jack Bashford Aug 24 '19 at 21:52
2

One option if it is the case that all constructor parameters are optional/have a default value would be simply to use a Partial config in the constructor and merge with defaults, e.g.

interface IPageConfigConstructor {
  isSliding: boolean;
  getList: (pagingInfo) => Observable<any>;
}
const DEFAULT_CONFIG:IPageConfigConstructor = {
  isSliding: true,
  getList: (pagingInfo) => null
}

class PageConfig {
  constructor(config: Partial<IPageConfigConstructor>) {
    const mergedConfig:IPageConfigConstructor = {...DEFAULT_CONFIG,...config}
  }
}

class UsersPage {
  config = new PageConfig({getList:(pagingInfo)=>this.getList(pagingInfo)});

  getList(pagingInfo) {
    // do work...
    return new Observable
  }
}
chrismclarke
  • 1,995
  • 10
  • 16