Implement JavaScript options object parameter pattern?

Here's a more exotic ES6 way to implement default options with destructuring assignment instead of Object.assign():

interface Options {
  foo?: string;
  answer?: number,
  aMethod?: (a:string) => string;
}

class Foo {
    options: Options;
    constructor({
            foo = 'bar',
            answer = 42,
            aMethod = undefined
        }: Options = {}) {
        this.options = { foo, answer, aMethod };
    }
}
var foo1 = new Foo();
console.log("foo1: foo", foo1.options.foo); // 'bar'
console.log("foo1: answer", foo1.options.answer); // 42
console.log("foo1: aMethod", foo1.options.aMethod); // function()
var foo2 = new Foo({answer: 0, aMethod: (a:string) => { return a; } );
console.log("foo2: foo", foo2.options.foo); // 'bar'
console.log("foo2: answer", foo2.options.answer); // 0
console.log("foo2: aMethod", foo2.options.aMethod); // function(a)

Further explanation: http://simonsmith.io/destructuring-objects-as-function-parameters-in-es6/


In Typescript

Let me chime in with a TS answer. In TS, you can of course define the type of the options object, but as of TS 3.2 you can't assign "partial defaults" in the function parameters (i.e. when unset properties of the object will be defaulted to some value).

So this is a real example of a web crawler function, using destructuring. You could also use Object.assign but then for type safety you would have to separately define the type interface for options.

All in all, the only caveat is that you will have to mention those properties twice which will be assigned a default value.

async downloadPageHTML(url: string, options: {
    cookies?: puppeteer.Cookie[],
    launchOpts?: LaunchOptions,
    pageLoadOpts?: Partial<puppeteer.NavigationOptions>,
    userAgent?: string
    browser?: puppeteer.Browser
} = {}) {
    let {
        pageLoadOpts = {},
        launchOpts = {},
        cookies = [],
        userAgent = getUserAgent(url)
    } = options;
    // ...
}

Seems as though you could do this with an Interface with optional members, and using Object.assign as you already have done:

interface Options {
  foo?: string;
  answer?: number,
  aMethod?: (a:string) => string;
}

class Foo {
    options: Options;
    constructor(options:Options) {
        this.options = {
            foo: 'bar',
            answer: 42,
            aMethod: function(){}
        };
        Object.assign(this.options, options);
    }
}
var foo1 = new Foo({});
foo1.options.foo; // 'bar'
foo1.options.answer; // 42
foo1.options.aMethod; // function()
var foo2 = new Foo({answer: 0, aMethod: function(a:string) { return a; } );
foo1.options.foo; // 'bar'
foo1.options.answer; // 0
foo1.options.aMethod; // function(a)

TS Playground Example

Tags:

Typescript