Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement JavaScript options object parameter pattern?

Tags:

typescript

There is a common pattern in JavaScript whereby a "constructor" accepts an optional options object. Additionally, that options object may contain only the options the caller wishes to override. For example:

function Foo(options) {
  this._options = {
    foo: 'bar',
    answer: 42,
    amethod: function(){}
  };
  this._options = Object.assign(this._options, options);
}

let foo1 = new Foo();
foo2._options.foo; // 'bar'
foo1._options.answer; // 42
foo1._options.amethod(); // undefined

let foo2 = new Foo({answer: 0, amethod: function(a) { return a; }});
foo2._options.foo; // 'bar'
foo2._options.answer; // 0
foo2._options.amethod('foo'); // 'foo'

Is it possible to implement this pattern in Typescript? If so, how?

like image 845
James Sumners Avatar asked Sep 18 '15 14:09

James Sumners


People also ask

What is the options object pattern in JavaScript?

The Options Object pattern [1] [2] is a technique in JavaScript for configuring a component using a single function parameter: This approach has many benefits over using multiple parameters to configure a function or constructor:

What is the options pattern in typescript?

The Options Object pattern [1] [2] is a technique in JavaScript for configuring a component using a single function parameter: This approach has many benefits over using multiple parameters to configure a function or constructor: TypeScript supports the pattern with strongly-typed options:

What are the different types of optional parameters in JavaScript?

Introduction to Optional Parameters 1 Method 1: Undefined arguments. At first glance, JavaScript has nothing like this available. ... 2 Method 2: The arguments variable. All JavaScript functions get passed an implicit arguments variable when they're called. ... 3 Method 3: The object literal. ... 4 A combination of all three methods. ...

What is the observer pattern in JavaScript?

The Observer pattern and its similar cousin the Publisher/Subscriber (a.k.a. Pub/Sub) pattern are elegantly simple and profoundly useful in all kinds of applications. The Observer pattern works by one object — the observer — subscribing to another object — the subject — by way of a callback method.


2 Answers

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

like image 109
CodingIntrigue Avatar answered Nov 07 '22 13:11

CodingIntrigue


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;
    // ...
}
like image 33
gombosg Avatar answered Nov 07 '22 14:11

gombosg