How does the browser know I'm targeting ES6? - javascript

I'm using Typescript and I recently changed the transpilation options to target ES6 instead of ES5.
I was using a certain syntax that had always worked fine under ES5, but after the target change, I started getting this error in the (Firefox) browser console:
Javascript ES6 TypeError: Class constructor Client cannot be invoked without 'new'
I changed the code to a suitable syntax for ES6, and this fixed the issue, but I don't understand how the browser would know to throw this error in the first place, because the exact same code worked before.
Does the javascript parser in browser scan other parts of the code base and see they they're using ES6, and then reject this line because it doesn't match the ES6 code elsewhere?

Javascript doesn't enter any particular "mode".
Likely what really happened is that you were declaring a class Client. That is ES6 syntax, and it comes with the special caveat that you must use new Client to instantiate it. Now, your compiler compiled this into a backwards-compatible function Client() .... Obviously, you can call a function without new. And that's what you were doing somewhere.
So, the compilation from class to function masked the error. But when leaving the class as class (because ES6 target mode does not need to dumb it down to a function), the browser was actually dealing with a class and raised that error.

Related

Can't set values on `process.env` in client-side Javascript

I have a system (it happens to be Gatsby, but I don't believe that's relevant to this question) which is using webpack DefinePlugin to attach some EnvironmentVariables to the global variable: process.env
I can read this just fine.
Unfortunatley, due to weirdnesses in the app startup proces, I need have chosen to do some brief overwritting of those EnvironmentVariables after the site loads. (Not interested in discussing whether that's the best option, in the context of this question. I know there are other options; I want to know whether this is possible)
But it doesn't work :(
If I try to do it explicitly:
process.env.myVar = 'foo';
Then I get ReferenceError: invalid assignment left-hand side.
If I do it by indexer (which appears to be what dotenv does) then it doesn't error, but also doesn't work:
console.log(process.env.myVar);
process.env['myVar'] = 'foo';
console.log(process.env.myVar);
will log undefined twice.
What am I doing wrong, and how do I fix this?
The premise behind this attempted solution was flawed.
I was under the impression that webpack "made process.env.* available as an object in the browser".
It doesn't!
What it actually does is to transpile you code down into literals wherever you reference process.env. So what looks like fetch(process.env.MY_URL_VAR); isn't in fact referencing a variable, it's actually being transpiled down into fetch("http://theActualValue.com") at compile time.
That means that it's conceptually impossible to modify the values on the "process.env object", because there is not in fact an actual object, in the transpiled javascript.
This explains why the direct assignment gives a ref error (you tried to execute "someString" = "someOtherString";) but the indexer doesn't. (I assume that process.env gets compiled into some different literal, which technically supports an indexed setter)
The only solutions available would be to modify the webpack build process (not an option, though I will shortly raise a PR to make it possible :) ), use a different process for getting the Env.Vars into the frontEnd (sub-optimal for various other reasons) or to hack around with various bits of environment control that Gatsby provides to make it all kinda-sorta work (distasteful for yet other reasons).

Ignore not declared variables (TypeScript)

I am creating a WebExtension using TypeScript which is later compiled to JavaScript.
My extension depends on one of the APIs the browser (Firefox) offers, specifically the extension API. As an example, I use the getURL() method, which is called like this:
browser.extension.getURL("foo/bar.js");
Of course, TypeScript gives an error "Cannot find name 'browser'". This prevents me from fully compiling the code. I would like to know if there is any way to bypass this. Preferably not only at compile level, but also at the linting level.
I have tried:
Defining browser at the beginning as var browser: any;: breaks the API.
Compiling with --noEmit, --noEmitOnErrors: irrelevant, still complains.
Any suggestions?
If you want to let Typescript know that the variable exists but not actually emit any code for it you can use declare
declare var browser: any;

qx.log.appender Syntax

When declaring qx.log.appender.Native or qx.log.appender.Console, my IDE (PyCharm) complains about the syntax:
// Enable logging in debug variant
if (qx.core.Environment.get("qx.debug"))
{
qx.log.appender.Native;
qx.log.appender.Console;
}
(as documented here)
The warning I get is
Expression statement is not assignment or call
Is this preprocessor magic or a feature of JavaScript syntax I'm not aware yet?
Clarification as my question is ambiguous:
I know that this is perfectly fine JavaScript syntax. From the comments I conclude that here's no magic JS behavior that causes the log appenders to be attached, but rather some preprocessor feature?!
But how does this work? Is it an hardcoded handling or is this syntax available for all classes which follow a specific convention?
The hints how to turn off linter warnings are useful, but I rather wanted to know how this "magic" works
Although what's there by default is legal code, I find it to be somewhat ugly since it's a "useless statement" (result is ignored), aside from the fact that my editor complains about it too. In my code I always change it to something like this:
var appender;
appender = qx.log.appender.Native;
appender = qx.log.appender.Console;
Derrell
The generator reads your code to determine what classes are required by your application, so that it can produce an optimised application with only the minimum classes.
Those two lines are valid Javascript syntax, and exist in order to create a reference to the two classes so that the generator knows to include them - without them, you wouldn't have any logging in your application.
Another way to create the references is to use the #use compiler hint in a class comment, eg:
/**
* #use(qx.log.appender.Native)
* #use(qx.log.appender.Console)
*/
qx.Class.define("mypackage.Application", {
extend: qx.application.Standalone,
members: {
main: function() {
this.base(arguments);
this.debug("Hello world");
}
}
});
This works just as well and there is no unusual syntax - however, in this version your app will always refer to the those log appenders, whereas in the skeleton you are using the references to qx.log.appender.Native/Console were surrounded by if (qx.core.Environment.get("qx.debug")) {...} which means that in the non-debug, ./generate.py build version of your app the log appenders would normally be excluded.
Whether you think this is a good thing or not is up to you - personally, these days I ship all applications with the log appenders enabled and working so that if someone has a problem I can look at the logs (you can write your own appender that sends the logs to the server, or just remote control the user's computer)
EDIT: One other detail is that when a class is created, it can have a defer function that does extra initialisation - in this case, the generator detects qx.log.appender.Console is needed so it makes sure the class is loaded; the class' defer method then adds itself as an appender to the Qooxdoo logging system
This is a valid JS syntax, so most likely it's linter's/preprocessor's warning (looks like something similar to ESLint's no-unused-expressions).
Edit:
For the other part of the question - this syntax most likely uses getters or (rather unlikely as it is a new feature) Proxies. MDN provides simple examples of how this works under the hood.
Btw: there is no such thing as "native" JS preprocessor. There are compilers like Babel or TypeScript's compiler, but they are separate projects, not related to the vanilla JavaScript.

Is there a `declare` keyword in ES6/7?

Recently I've been studying ES6, and that lead to me using Babel a lot. Being the curious type, I started looking at the Babel Github repository to know how they built this awesome tool, and know if I can somehow contribute.
However, I came across this file, and it has things like declare class BabelNodeSourceLocation {} written all over it, and the file ends with .js.
This got me very confused, and I am now wondering whether there's a declare keyword in JavaScript that I didn't know of, or is this just a Babel-specific syntax? All my Google searches resulted in nothing.
Update: Putting the code in the Babel REPL resulted in nothing. Babel just ignored the code and did not produce any equivalent ES5 output. It also did not throw any error.
and the file ends with .js.
That doesn't mean a lot these days :-)
I am wondering whether there's a declare keyword in JavaScript that I didn't know of
No, there is not.
Or is this just a Babel-specific syntax?
No. It is a type declaration file for the Flow typechecker.
With Flow, you can declare a global class that allows you to reference the class type anywhere in your project. This has no affect on runtime code and won't affect babel output.
An example from the docs:
declare class URL {
constructor(urlStr: string): URL;
toString(): string;
static compare(url1: URL, url2: URL): boolean;
};
And then in your project you can reference URL as a class type.
Similarly, you can declare other global Types, Modules, Functions, Variables. A good way to keep them organized.

Is TypeScript really a superset of JavaScript?

I just started using TypeScript and sometimes get compiler errors "use of undeclared variable". For example the following works in plain JavaScript :
var foo = {};
foo.bar = 42;
If I try to do the same in TypeScript it won't work and give me the mentioned error above. I have to write it like that:
var foo :any = {};
foo.bar = 42;
In plain JavaScript the type definition with any is neither required nor valid, but in TypeScript this seems to be mandatory. I understand the error and the reason for it, but I always heard in Videos and read in the documentation:
typescriptlang.org:
"TypeScript is a typed superset of JavaScript [...]"
Introduction Video #minute 3:20:
"All JavaScript code is TypeScript code, simply copy and paste"
Is that a thing that changed during the development of TypeScript or do I have to pass a specific compiler setting to make this work?
The reason for TypeScript's existence is to have a compiler and language which can enforce types better than vanilla Javascript does. Any regular Javascript is valid TypeScript, syntactically. That does not mean that the compiler must be entirely happy with it. Vanilla Javascript often contains code which is problematic in terms of type security. That doesn't make it invalid TypeScript code, but it's exactly the reason why TypeScript exists and it's exactly the compiler's job to point out those problems to you.
The languages as such are still sub/supersets of one another.
Theorem: TypeScript is neither a subset nor a superset of JavaScript.
Proof:
When we say language A is a subset of language B, we mean all valid A-programs are also valid B-programs.
Here is a valid TypeScript program that is not a valid JavaScript program:
let x: number = 3;
You identified a valid JavaScript program that is not a valid TypeScript program:
var foo = {};
foo.bar = 42;
Complicating factor 1: TypeScript is almost a superset. TypeScript is intended to be a near superset of JavaScript. Most valid JS is also valid TS. What JS is not can usually be easily tweaked to compile without errors in TS. In other words, most valid JS is also valid TS.
Complicating factor 2: non-fatal errors The TypeScript compiler generates the JavaScript code you intend sometimes even if there are errors. The your example that I referenced earlier emits this error
error TS2339: Property 'bar' does not exist on type '{}'.
but also this JS code
var foo = {};
foo.bar = 42;
The TS documentation notes
You can use TypeScript even if there are errors in your code. But in this case, TypeScript is warning that your code will likely not run as expected.
I think we can call this a failed compilation (and thus invalid TypeScript) for the following reasons:
The compiler seems to use the term warning in the conventional sense, so we should interpret error in the conventional sense too: an error indicates the compilation failed.
The documentation indicates that the resulting JavaScript is not necessarily correct as to what was intended. An incorrect output seems just as bad as (if not worse than) no output. They should both be considered failed.
The TypeScript compiler exits with a non-zero status code, which conventionally indicates that the process failed in some way.
If we call any TypeScript program that outputs JavaScript "valid", then we would have to call the following TypeScript program valid, because it a dot compiles to the empty string after issuing errors:
.
Complicating factor 3: TS accepts JS files: The TypeScript compiler can passthrough files ending in .js (see compiler documentation for --allowJs). In this sense TypeScript is a superset of JS. All .js files can be compiled with TypeScript. This is probably not what people who visit this question are meaning to ask.
I think complicating factor 1 is the thing that Anders Hejlsberg is getting at. It might also justify the misleading marketing on TypeScript's homepage. The other answers have fallen prey to complicating factor 2. However the general advice given in the other answers is correct: TypeScript is a layer on top of JavaScript designed to tell you when you do something bad. They are different tools for different purposes.
No. In other answers I believe the technical reason has been well explained, but I notice an example that could immediately serve as a contradiction to the claim in the question (different semantics):
// In TypeScript
function f<A>(a: A) { return a; };
console.log(f<Function>(f)); // <-- This line. It will print the function f since it is an identify function that in this case takes self and returns self.
Comparing to the below JavaScript example
// In JavaScript
function f(a) { return a; };
console.log(f<Function>(f)); // <-- This line. This is VALID JavaScript
At first glance you might think there should be a syntax error for the JavaScript example. HOWEVER, once you examine it closely, you'll notice that actually the line is executed as
console.log((f < Function) > f); // Evaluate to false
which is completely valid in JavaScript. This essentially means the same line of code resulted in 2 completely different interpretation in JavaScript and TypeScript, therefore a counterexample to the question.
The definition
"All JavaScript code is TypeScript code, simply copy and paste"
is true. Because any JavaScript code can passed to the TypeScript compiler.
So it's sort of a Layer on top of JavaScript. So, of course the underlaying Layer (JavaScript) can be passed through the layers to the top (TypeScript), but not the other way around.
Why not?
Think of it as a bike (JavaScript) and a motorcycle (TypeScript). The basics are the same (two wheels, a frame), but the motorcycle as an engine and some enhanced features.
So, you can use your motorcycle (TypeScript) as a bike (JavaScript), but you cannot use a bike as a motorcycle.
EDIT:
If your compiler throws a warning, why does it make the
statement wrong? It just says: Hey, you are using TypeScript, and it's
more strict than what you gave me.
See this example, it compiles perfectly to JavaScript, but throws a warning.

Categories

Resources