Interface in Vanilla JavaScript
TL;DR Here's what an interface looks like in Vanilla JavaScript, using VS Code IntelliSense: var interface = () => null; var InterfaceOptions = () => ({ name: '', }); InterfaceOptions = interface; let opt = InterfaceOptions`` ?? { name: 'Bagel', }; function createItem(options = InterfaceOptions``) { // ... } createItem(opt); Discovery 1) I want VS Code IntelliSense to suggest the "shape" of the createBox() options. 2) Using default parameter works, but I want to place it somewhere else to declutter a bit. 3) Declaring the options outside the function creates a bug because anyone can tinker with the value. 4) So It must be an object builder. On line 5, I use backticks instead of parentheses to differentiate an "interface" from a function invocation. Actually, I should just use a unique prefix for the variable name such as InterfaceBoxOptions or something for this post, oh well. 5) Okay, that works, but what if I declare the options as their own variable? How am I supposed to tell IntelliSense that an object has the shape of an interface? 6). As you may know, IntelliSense assumes the interface shape if I first assign the interface to the object. 7) Apparently, though, it still works even after reassign the variable itself with a new object. Now, that's wild west! 8) But this looks too cluttered. Can I make it a one-liner at least? 9) Answer is yes, using the nullish coalescing (??) operator. This is the only way I’ve found. One problem though, to assign the new object instead of the interface, I need to somehow make the boxOptions returns null. 10) Luckily—or perhaps intentionally by design—IntelliSense keeps suggesting the initial shape of the interface even after reassigning it to a function that returns null. And just like that, I've got a working interface-like setup in vanilla JavaScript. Should probably use typescript from the start, but I belong to the wild west. In Production For object declaration, I write a build script to replace interfaceName ?? with empty string before passing it to Terser because the compressor can't tell that it is unused. Before: let opt = InterfaceOptions`` ?? { name: null, } After: let opt = { name: null, } Compressed code if you don't remove the interface: let opt = () => null ?? { name: null, } Trivia 1. Use Var for Interfaces For interfaces, you want to use var instead of letor const. This makes sure it got removed when you compress at top level using Terser. var interface = () => null; var InterfaceOptions = () => ({ name: null, }); InterfaceOptions = interface; // terser options { toplevel: true, compress: true, // ... } 2. Null Interface Alternative If global interface function is not available, e.g. if you're writing library for someone else, instead of this: var interface = () => null; var InterfaceOptions = () => ({ name: null, }); InterfaceOptions = interface; you can do this: var interface = () => null; var InterfaceOptions = () => ({ name: null, }); InterfaceOptions = () => null;
TL;DR
Here's what an interface looks like in Vanilla JavaScript, using VS Code IntelliSense:
var interface = () => null;
var InterfaceOptions = () => ({
name: '',
}); InterfaceOptions = interface;
let opt = InterfaceOptions`` ?? {
name: 'Bagel',
};
function createItem(options = InterfaceOptions``) {
// ...
}
createItem(opt);
Discovery
1) I want VS Code IntelliSense to suggest the "shape" of the createBox()
options.
2) Using default parameter works, but I want to place it somewhere else to declutter a bit.
3) Declaring the options outside the function creates a bug because anyone can tinker with the value.
4) So It must be an object builder. On line 5, I use backticks instead of parentheses to differentiate an "interface" from a function invocation. Actually, I should just use a unique prefix for the variable name such as InterfaceBoxOptions
or something for this post, oh well.
5) Okay, that works, but what if I declare the options as their own variable? How am I supposed to tell IntelliSense that an object has the shape of an interface?
6). As you may know, IntelliSense assumes the interface shape if I first assign the interface to the object.
7) Apparently, though, it still works even after reassign the variable itself with a new object. Now, that's wild west!
8) But this looks too cluttered. Can I make it a one-liner at least?
9) Answer is yes, using the nullish coalescing (??
) operator. This is the only way I’ve found. One problem though, to assign the new object instead of the interface, I need to somehow make the boxOptions
returns null
.
10) Luckily—or perhaps intentionally by design—IntelliSense keeps suggesting the initial shape of the interface even after reassigning it to a function that returns null
.
And just like that, I've got a working interface-like setup in vanilla JavaScript. Should probably use typescript from the start, but I belong to the wild west.
In Production
For object declaration, I write a build script to replace interfaceName ??
with empty string before passing it to Terser because the compressor can't tell that it is unused.
Before:
let opt = InterfaceOptions`` ?? {
name: null,
}
After:
let opt = {
name: null,
}
Compressed code if you don't remove the interface:
let opt = () => null ?? {
name: null,
}
Trivia
1. Use Var for Interfaces
For interfaces, you want to use va
r instead of let
or const
. This makes sure it got removed when you compress at top level using Terser.
var interface = () => null;
var InterfaceOptions = () => ({
name: null,
}); InterfaceOptions = interface;
// terser options
{
toplevel: true,
compress: true,
// ...
}
2. Null Interface Alternative
If global interface function is not available, e.g. if you're writing library for someone else, instead of this:
var interface = () => null;
var InterfaceOptions = () => ({
name: null,
}); InterfaceOptions = interface;
you can do this:
var interface = () => null;
var InterfaceOptions = () => ({
name: null,
}); InterfaceOptions = () => null;