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?
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:
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:
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. ...
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.
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
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;
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With