The idea of adding types to ES has been around for quite some time, but there's no real interest in overhauling the engines at the level required to make it happen. It's understandable. So, what if there's a way to introduce a form of typing that shouldn't require so much work?
Objective:
Provide a means of constraining the type of a variable.
Minimum Requirements:
- Where the desired type is primitive, it should only allow values of that primitive type to be assigned.
- Where the desired type is an object, it should only allow values possessing the same prototype to be assigned.
The Nutshell:
Instead of trying to find a way to implement types cheaply without butchering load times, why not implement type fixing? What people want from typing is the speed increases that come along with a guarantee that the shape of a variable will not change. Right now that's being weakly handled by making things immutable, but that puts more stress on the garbage collector and can sometimes actually cost performance while the GC cleans up.
What I suggest instead is a keyword of some kind that can flag a variable as being unable to alter its type. Here's an example.
/* The "fixed" keyword tells the engine to only allow values of the
* initialized type to be assigned to the specified variables. Attempts to
* do otherwise throws a TypeError.
*/
function percentChanged(fixed oldVal = 0, fixed newVal = 0) {
return 100 * (newVal - oldVal) / oldVal;
}
//Both val1 & val2 are fixed as numbers
let fixed val1 = ~~(Math.random() * 100), val2 = ~~(Math.random() * 100);
console.log(`val2 differs from val1 by ${percentChanged(val1, val2)}%`);
//Assuming the field exists....
let val3 = document.queryElement("someField").value;
try {
console.log(`someField differs from val1 by ${percentChanged(val1, val3)}%`);
}
catch(e) {
console.log(`Guess somefield.value wasn't a number... Should've validated first.`, e);
console.log(`someField differs from val1 by ${percentChanged(val1, parseFloat(val3))}%`);
}
The fixed
keyword requires that an initializer be provided. The type of the initializer becomes the static type of the variable. Objects are a little trickier but still follow similar logic. Since the "type" of an object is usually defined by its prototype, if the initializer value of a fixed
variable is an object, all assignments to the variable would require that the new value is an object and that the prototype of the initializer be in the prototype chain of of the new value.
It should also be possible to type fix member properties of an object.
let a = {
fixed alpha = 0,
fixed beta = "",
fixed gamma = true,
fixed delta = Symbol(),
fixed epsilon = {}
}
class b {
static fixed #alpha = 0;
fixed #beta = "";
fixed gamma = true;
fixed delta = Symbol();
fixed epsilon = {};
}
Limitations:
The fixed
keyword:
- must throw a TypeError if the determined "type" is
null
orundefined
. This is also the reason whyfixed
requires an initializer. - must not perform any coersion on assignment. The type of the value being assigned must already match the type of the
fixed
variable or a TypeError will be thrown.
TL;DR:
If we have a way of telling the engine that it doesn't need to worry about the type of a given variable changing, that should give us all the benefit of static typing with very little cost. No new types need to be defined in the engine, and the parsing cost is relatively low. This means very little additional overhead to parse.
What do you think?