bring strict type into javascript / ecmascript

Hello, I was wondering if it would be possible to add strict type feature to the language.

strict type can be denoted as strict mode is by string literal

"strict type mode"

in this declare environment, the word "type", "strict", or better yet use constructor to denote the type, shall be treated as a keyword to denote a variable as strict, not changing type, if assign number, it can only be assigned number.

type num = 0;
strict num = 0;

if we use constructor to denote the type than the examples above shall be set to default false value of that type if no value is set at initialization for examples

Number num;// is the same as Number num = 0;
String str;// is the same as String str = "";
Object obj;// is the same as Object obj = null;

num = 1;//good
num = "a";//ERROR: variable cannot change type in strict type mode

This will make the language easy to compile to other languages and even to wasmjs possible.

coffescript and typescript are attempts to add this feature, and yes to a language like JavaScript, this is a feature as all the other features of this beautiful language made in chaos and rush.

I think this will do great for developers of dynamic and strict types of languages going into learning JavaScript.

I can imagine one of you coming up with this already, but it has been on my mind to at least attempt to get this feature requested, and so this is my attempt!

Keep up the good work, and thank you for advancing javascript / ecmascript!

There's been a handful of different discussions around this idea on this form, so if you want to read up a bit on prior discussion, you can do a quick search for "type" and click on anything that sounds interesting. In particular, this thread proposes a somewhat similar idea, about adding support to set variables to specific types.

I would like to ask for a bit of clarification on your idea. If I write this code:

function doThing(String myStr) { ... }
doThing(2)

Is this a runtime error? Or a loadtime error? Or both (it checks for type errors at loadtime, and at runtime, in case it missed anything)?

Also, sorry, I have to nitpick a bit - CoffeeScript didn't actually provide any type-related features, it was merely an alternative syntax for JavaScript (it provided things like class syntax and optional chaining long before JavaScript had them)

1 Like

Didn't TC39 already reserve that types should be defined like so:

function doThings(myStr: String) { ... }
class Ex {
   data: Type = value
   ...
}

to allow for TypeScript to work without risking interference with future ES development? Any proposal that gets accepted will probably have to follow this syntax. The thing I don't know is what types look like when applied to object definitions.

@rdking Why must we restrict the syntax to what is being used to translate to javascript? Plus typescript is used to enforce type, so it should be used only with javascript dynamic type instead of strict type mode.

if we denote strict type by a string literal then typescript can just throw an error saying typescript is for javascript dynamic type mode and cannot compile to javascript strict type mode. That is an option, if javascript had type then typescript wouldn't exist. It's a language made to add support of type to javascript or really enforce you do not change the variable's type.

But if what you said is true then what you say probably will come to pass since they spoke on it already and considering other languages and how it might get affected by the changes to ecmascript.

@theScottyJam maybe both. Good questions, so should it throw an error when the page load as if it is like a syntax error, or should it run the error when in runtime. Both, it would be both causes since dynamic type is a part of the language var, let const can still be used in "strict type mode" giving freedom to write the code as normal, and not only that, we can't test for everything during load time like resource HTTP fetch sources. Yes, it will have to be both regardless.

function doThing(String myStr) { ... }
doThing(2)

can the language see what type of data source is coming from web API? Or is that a black box to a language? I think both if possible in my opinion.

Considering the popularity of TypeScript and Flow in the JS ecosystem, and also looking at other more recent new languages (go, kotlin, swift). There does seem to be a strong general preference to have (symbol: type) over (type: symbol)

1 Like

@aclaymore I guess I should start learning typescript to get a jump start to the future of javascript syntax or something similar. Also like you said popular, there are applications, platforms that adopt typescript right alongside javascript. I didn't know it was that big.

Just to reinforce, TypeScript's bigger than you'd think.

@theScottyJam @aclaymore

So I'm coding in javascript and I'm just thinking how simple javascript is... it has it bad but it is simple... I mean you put above a script in string literal "use strict" and it switches the whole context of the language almost. I was just wondering since I'm getting into Godot for security reasons...I was wondering can we just use "use type strict" to change nothing of the languages but to intlize a variable you will have to set it in this mode but everything else is the same.

To initialize a variable you will have to set it

`strict type mode`//honestly don't care what the string literal we have to type say
let str = "";//okay
let num; //ERROR: type not set

probable some complex reason why the engine can't force these rules but just wanted to express my thoughts.

you have shown me a graph with the language of this whole website focus on developing at the bottom...

which makes some sense for my reply to the learning typescript.

Well, here's my personal issue with this idea. Currently, there seems to be two coding styles when it comes to variable declarations.

  1. A looser style - use "let" everywhere.
  2. A more strict style, use "const" everywhere it's possible (which is almost everywhere).

I personally prefer the second style, but I can also understand the appeal for the first. What this proposal is basically doing, is adding something in between 1 and 2. When you're in "strict type mode", you'd use let everywhere, and if you choose to reassign, you're only allowed to reassign to a value of a different type. You'll notice that this proposal provides no advantage to people like me who use const everywhere, because we're already not reassigning to our variables, and all this does is lock down what you're allowed to reassign.

In the end, even if you use let everywhere, you'll find that you don't reassign that often, plus, it can be considered good practice to avoid reassignment, even if you bother to explicitly mark what you're doing with const. So, in my mind, if you're not worried about this sort of thing, use let, but if you are, use a linter to enforce const and use that everywhere, I'm not sure that there's really enough need to have this in-between step.

The other issue I have, is that I'm not sure how effective this can be at doing its job. How do you determine the initial type of something? JavaScript is incredibly dynamic, so it's difficult to really know what the types of different values are ahead of time. Take these examples:

let x = condition ? 'x' : 2
let w = [2, 'x'][someIndex]
let y = globalThis.someFunctionSomeoneElseProvides()
Array.prototype.map = () => 'Hello World!'
let z = [].map() // z will hold "Hello World!", which is a string, not an array
1 Like

While I understand the movement towards mutation-free code, I can't help but find it somewhat pointless. When all is said and done, a computer is just a state machine mutating both internal and external storage. So I can only say that arguments touting this style as "good practice" are at best overstating the benefit of that approach.

Ignoring that, you're still missing some important considerations in your argument: functions.

function someFunctionSomeoneElseProvides(String oneArgument, BigInt anotherArg) {
   ...
}

Calling this function would require you to put in 2 arguments of the corresponding types, or at least types easily converted by the constructors. This is one of the main reasons for adding type support.

@theScottyJam I don't understand your point, what good would it do? and you mention const which I'm more confused about.

@rdking and the former mentions

my point is to just force defining the type at initializing the variables as we can already do in javascript. It might no point in adding more syntax when you can use the syntax that is already there and just enforce best practices of never changing a variable data type.

What we can do already in javascript:

let str = "", num = 0, obj = {};

function func(str = "", num = 0, obj = {}){
   return "";
}

What we can force programmers to do in "use strict type" javascript:

"use strict type"
let str = "", num = 0, obj = {};//these object can't be change type

function func(str = "", num = 0, obj = {}){//the type that pass to this is str, num, obj
   return "";//this function return only string type;
}

and all this "use strict type" will do is force you never to change a variable type, so that compiler or other tools can safely work with the script as if it is a strict type language when they see the string literal denoting.

Now we can introduce a class object call Reference that allows the programmer to Reference unknown datatype when they know what it could be... so Reference will have a way for you to gain information of what class object is, what data type, and allow the programmers to switch it to the correct type variables for more optimizations or to just reference it. And I think we already got that with symbols but I'm not sure I don't really work with them, I think they're more of an internal use case. But if Symbols or a javascript native object already have this ability behind the black box of whatever implantation it is for all implantations then we can expose that to the programmer and get them to div more in the language of what's already here.

Last time I heard, every engine developer in TC39 will likely reject any attempt to add more directives to ES. They say it mucks too heavily with optimization. So in the end, you're going to be stuck with some kind of syntactic change to add types. I once had a similar thought as you, except I recognized the problem I just described, so I put up the type-fixing thread. At bare minimum, its going to require either a new keyword or new syntax.

Well, as @rdking mentioned, const doesn't actually help with function parameters. But, for plain declarations, it'll provide the same restriction you want, and more.

const str = "", num = 0, obj = {}; // these value can't be change type, because, well, you can't reassign to them

Some people use const everywhere, others do not, and that's totally fine.

1 Like

@rdking - I do want to point out that it's one thing to not want to mutate values themselves, and it's a different thing to not want to reassign. I can understand if people are hesitant with the whole "make everything immutable" idea, because writing code like that does make your code more verbose. But, when it comes to reassignment, there's not that much to be gained from allowing yourself to reassign and it's trivial to give each variable a new name instead of reusing old declarations, especially once the pipeline operator is out, which lets you avoid naming intermediate steps of an algorithm - this idea applies both to let users and const users, as either can avoid reassignment with little effort.

That said, I have no problem if you don't feel this same way, and find const to be overhyped.

1 Like

Like you, I see a significant difference between mutating values and restricting re-assignment. They each have their purposes. Even in ES, I tend to use variables as if they have an intrinsic and immutable type. Even when I don't follow that tendency, I'm treating the variable as a Variant, and restricting the use of that variable to a particular context.

The use of const does have a purpose when creating logic intended to be used by other developers. I honestly wish you could use it in an object declaration as well. Kind of like this:

let a = {
   const foo: "data"
};

//Same as
let a = Object.create(Object.prototype, {
   foo: {
      enumerable: true,
      value: "data"
   }
});

As far as the use of const in non-exported code? Almost entirely pointless. The only value is to catch mistakes made by the developer. While a very limited use, that is still a good enough use to make the keyword valuable. So just as you say:

I would also say that there's not much to be gained from restricting reassignment in most scenarios. However, reassignment can lead to space optimization in both code and memory as well as certain types of runtime optimization that cannot be achieved by throttling the heap as would be done without reassignment.

TL;DR, Tools in a drawer. There are times when I create code that doesn't allow for reassignment, and times when I do the opposite. It's the job of the developer to recognize when a given paradigm is suitable for a given scenario. Blanket policies enforcing the use of one particular paradigm over another are inevitably and invariably wrong.

1 Like

FWIW many mainstream compilers (e.g LLVM) will actually first convert all code so that all variables are only assigned once to simplify optimising the program before generating machine code. Static single-assignment form - Wikipedia

Is there any wonder why we keep needing faster computers with more memory just to do a little more at the same speed? :rofl:

That's just another example of using tools inappropriately. If the goal is to make the compiler easier to develop, then good job. However, if the goal is to make more efficient programs, then... well....

I think it's better to take a pythonic approach here when bringing types to an extremely dynamic language.

In python, you can annotate types along with your declarations from version 3.10.2 (I guessπŸ˜…).

As the official docs says:

The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.

There's this type hints proposal (sorry, I couldn't find a link to it) that aims to bring this exact same feature to JS.

i.e
The TypeScript like annotations will be considered as comments when the code is executed.

let name: string = "Bob"
// same as
let name /*string*/ = "Bob"

My thought is that once this feature becomes widely used, we can introduce some way to statically enforce types natively (maybe with some performance overhead) by the JS engine.

like:

"use types";
let name: string = Symbol("Bob") // Error

The pragma is just a suggestion.

I don't think it would be a good thing to add something pointless to the language. If adding types doesn't do anything useful when first released, developers will simply skip them in the interest of typing less and producing smaller script files. It's utterly amazing how much "ergonomics" plays into the decisions made for this language. So at the very least, a type specification must actually enforce assignment coercion.