Agreed. And unless I'm reading the document wrong, this would also be prohibited in strong mode:
let x = {
keyA: "valueA"
};
x.keyB = "valueB"; let x = {
keyA: "valueA"
};
x.keyB = "valueB";[Maps]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
For what it's worth, you can still use "options hashes" in your argument APIs in strong mode using objects; you just write a library function something like:
function options(options, defaults) {
// the final args start off as a clone of the default args
let args = Object.clone(defaults);
// we then loop through the keys and copy in any overrides
for(let key of Object.keys(args)) {
// ignore inherited properties and skip missing ones
if(args.hasOwnProperty(key) && options.hasOwnProperty(key)) {
args[key] = options[key];
}
}
// args now has all of the overrides from options
return args;
}
And then in all your functions that take options objects: function bakeBread(ingredientOverrides) {
let ingredients = options(ingredientOverrides, {
flourType: 'whole wheat',
sugarAmount: '3 tbsp',
waterAmount: '1 cup',
milkAmount: '0.3 cups',
flourAmount: '4 cups'
});
let batter = mix(ingredients);
return bake(batter);
}
JS is still quite dynamic, even in strong mode — you can define arbitrary objects and types at runtime, and easily inspect/reflect on them — it's just a little harder to silently corrupt data.The neat thing about using named arguments with objects is that in typed variants of JS — for example, TypeScript, or perhaps someday SoundScript — you can actually typecheck them! Maps can't do that in any language I know of: by design they can contain anything.
Map<String, Integer> = new HashMap<String, Integer>();
That declares a Map with a String key and Integer value. Is this what you're thinking of? interface BreadIngredientOptions {
flourType?: String; // this is the syntax for optional strings
sugarAmount?: String; // ditto: it's the ? that makes it optional
// ...
}
function bakeBread(ingredientOverrides: BreadIngredientOptions) {
// ...
}
// callers don't need to explicitly inherit or implement to be type checked
// however, since all properties are optional, this is less interesting
bakeBread({
flourType: 'white'
});
But you can do even better than that example shows. One common problem with maps-as-named-arguments is that you can't easily determine which arguments are required and which are optional. With typed optional properties and structural subtyping you can enforce that at compile time, as follows: interface MyArgumentInterface {
requiredArg: number;
optionalArg?: number;
}
function f(args: MyArgumentInterface) {
// ...
}
// This works:
f({
requiredArg: 10,
optionalArg: 5
});
// This also works:
f({ requiredArg: 50 });
// This statically throws at compile time:
f({ optionalArg: 10 });
It's a combination of the simple object literal syntax from raw JS that makes it easy to create objects of arbitrary types, with structural subtyping. I'm not aware of any language with the same features (but would love to be corrected!).