Skip to content

Instantly share code, notes, and snippets.

@queerviolet
Last active February 9, 2025 15:32
Show Gist options
  • Save queerviolet/f5d84865129a17711d7bef661fbb247d to your computer and use it in GitHub Desktop.
Save queerviolet/f5d84865129a17711d7bef661fbb247d to your computer and use it in GitHub Desktop.
Type Annotation Comments in JS
/**
* I like annotating my functions with comments that contain type annotations.
* The annotation syntax I use is, obviously, not an actual language, but it's
* strict enough to be one. Perhaps some day I'll write a yet another type flow tool
* to check these things!
*
* The syntax is a mishmash of Swift and JS destructuring expressions.
**/
/***** Basic format *****/
// x: Number
const x = 5 // I rarely type annotate individual variables, but if I did, that's how it would look.
// sum(a: Number, b: Number) -> Number
//
// sum takes two Numbers, a and b, and returns their sum, also a Number.
// a and b must be present and may not be null.
//
// (Note that we don't actually check this in the function, but if it's not
// true, the caller is violating the contract and we make no guarantees as
// to what will happen).
function sum(a, b) { return a + b }
// sumMaybeNumbers(a: Number?, b: Number?) -> Number
//
// sum takes two numbers, a and b, which may be null, and returns a Number.
function sumMaybeNumbers(a, b) {
return (a || 0) + (b || 0)
}
// sumArray(nums: [...Number]) -> Number
//
// sumArray takes an array of numbers and returns their sum, also a Number.
// We represent an array of numbers with the type [...Number], inspired by the
// rest operator.
function sumArray(nums) { return nums.reduce((x, y) => x + y) }
// increment(nums!: [...Number]) -> [...Number]
//
// increment takes an array of numbers and modifies it by adding one to each
// element in the array.
//
// We use `nums!` to indicate that it will be modified.
function increment(array) {
let i = array.length; while(--i >= 0) { --array[i] }
return array
}
// read(file: String, done: (String)->any?) -> undef
//
// read takes a String filename and a done function.
// done takes a String (the file content) and returns anything.
function read(file, done) {
fs.readFile(file, (err, ok) => ok && done(ok.toString()))
}
/**** Composite types ****
* Composite types look like object literals, only they're made of types.
**/
// area(box: {width: Number, height: Number}) -> Number
//
// area takes a box (which is an object that must have a width and height, both Numbers),
// and returns its area.
function area(box) { return box.width * box.height }
/**** Type definitions ****
* If I use a particular composite type a lot, I might want to give it a name.
* The syntax I use is:
*
* Size = {width: Number, height: Number}
*
* Which would now let me say,
*
* area(box: Size) -> Number
*
* To describe the area function above.
**/
/**** Type algebra ****
* You can express that a parameter may be the intersection or union of different types with
* the & and | operators
**/
// sumNumOrStr(a: Number|String, b: Number|String) -> Number
//
// Return the sum a and b. If strings, they will be converted
// to numbers.
function sumNumOrStr(a, b) { return (+a) + (+b) }
// Size = {width: Number, height: Number}
// Position = {x: Number, y: Number}
// Bounds = {top: Number, left: Number, bottom: Number, right: Number}
// bounds(box: Size & Position) -> Size & Position & Bounds
//
// box must have all the properties {width, height, x, y}, all Numbers.
// A new object is returned with the box's size and position and computed bounds.
function bounds(box) {
return Object.assign({}, box, {
top: box.y,
left: box.x,
bottom: box.y + box.height,
right: box.x + box.width
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment