Automatic global type augmentations with intellisense
If you ever wanted to add methods to a built-in type like Number
or String
in JavaScript, you could extend the prototype directly:
// Add helper function to number Object.defineProperty(Number.prototype,'clamp',{ enumerable: false, value: function(min, max) { return Math.min(Math.max(min, this), max); } }); // Add setter to allow defining shortcuts for dom elements Object.defineProperty(HTMLElement.prototype,'shortcut',{ enumerable: false, set: function() { this._shortcut = value; // register key-bindings etc } });
Now, if we try to use these functions, the type-checker will flag it as errors, and intellisense will not work:
(10).clamp(5,15) // Property 'clamp' does not exist on type number let el = document.createElement('my-component'); el.shortcut = 'ctrl+a' // Property 'shortcut' does not exist on type HTMLElement
To enable type-checking and intellisense you will have to create a separate file where you declare the added methods:
// types/extensions.d.ts declare global { interface Number { clamp(min:number, max: number) : number; } interface HTMLElement { set shortcut(value: string); } }
Now, if you make sure the .d.ts
file is referenced in your project, the squiggly lines should disappear, and completions should start to work!
It is not considered good practice to extend global types like this anyways, but extending (re-opening) your own classes, and augmenting interfaces of external libraries is even more clunky, and there might be good reasons for you to do it.
In Imba, where dom elements are first-class citizens and it is rather easy to create large projects that do not depend on a bunch of external web components and libraries, extending the functionality of tags and objects is not discouraged. This is how you would do it in imba:
extend class Number def clamp(min
umber, max
umber) return Math.min(Math.max(min,self),max) extend tag element set shortcut value # register key-bindings etc let el = <div shortcut='ctrl+a'> <span> 10.clamp(5,15)
That is all you need. Imba generates the correct typescript declarations (with type inference). Type-checking, goto definitions, auto-completions etc just works. If your project includes a mix of imba, js, and typescript it will work across all your files.
10.clamp(5,15) let el = <div shortcut='ctrl+a'>
This is even better than it seems. Imba also does type inference from your actual declarations, which makes things a lot less verbose. Let's allow all custom components to easily access a shared api:
import API from './api' const api = new API extend tag component get api api
Now, all components Imba will have direct access to the api. Again, with intellisense.
# define a custom button tag edit-user-button < button <self @click=api.editUser(data)> 'edit user' # use it in another custom component tag user-modal <self> <h1> "User" <.actions> <edit-user-button data=user> ... # No need to pass the api down into children, or import it from every file.
If you want to add functionality to your api without writing it all in one file, you can simply extend the class:
import API from './api' extend class API def broadcast eventstring, data = {} # do something here ... self
If you would like to know more about Imba, read the latest dev.to post or go to imba.io 🙂