Typescript transpires to JS. Then there is tree shaking, "less" (optional) and what else in the process of making a deployment. But nothing like that (afaik) has anything to do with "compiling". Everything gets bundled and heavily optimized, but it's not actually compiled, right?
Compilation means transforming a program written in a language A into a semantically equivalent program written in language B such that evaluating the compiled program according to the rules of language B (for example interpreting it with an interpreter for B) yields the same result and has the same side-effects as evaluating the original program according to the rules of language A (for example interpreting it with an interpreter for A).
Compilation simply means translating a program from language A to language B. That's all it means. (Also note that it is perfectly possible for A and B to be the same language.)
In some cases, we have more specialized names for certain kinds of compilers, depending on what A and B are, and what the compiler does:
- if A is perceived to be assembly language and B is perceived to be machine language, then we call it an assembler,
- if A is perceived to be machine language and B is perceived to be assembly language, then we call it a disassembler,
- if A is perceived to be lower-level than B, then we call it a decompiler,
- if A and B are the same language, and the resulting program is in some way faster or lighter, then we call it an optimizer,
- if A and B are the same languages, and the resulting program is smaller, then we call it a minifier,
- if A and B are the same languages, and the resulting program is less readable, then we call it an obfuscator,
- if A and B are perceived to be at roughly the same level of abstraction, then we call it a transpiler, and
- if A and B are perceived to be at roughly the same level of abstraction and the resulting program preserves formatting, comments, and programmer intent such that it is possible to maintain the resulting the program in the same fashion as the original program, then we call it a re-engineering tool.
Also, note that older sources may use the terms "translation" and "translator" instead of "compilation" and "compiler". For example, C talks about "translation units".
You may also stumble across the term "language processor". This can mean either a compiler, an interpreter, or both compilers and interpreters depending on the definition.
Javascript itself is still interpreted, right?
JavaScript is a language. Languages are a set of logical rules and restrictions. Languages aren't interpreted or compiled. Languages just are.
Compilation and interpretation are traits of a compiler or interpreter (duh!). Every language can be implemented with a compiler and every language can be implemented with an interpreter. Many languages have both compilers and interpreters. Many modern high-performance execution engines have both at least one compiler and at least one interpreter.
These two terms belong on different layers of abstraction. If English were a typed language, "interpreted-language" would be a type error.
Note also that some languages have neither an interpreter nor a compiler. There are languages which have no implementation at all. Still, they are languages, and you can write programs in them. You just can't run them.
Also, note that everything is interpreted at some point: if you want to execute something, you must interpret it. Compilation just translates code from one language to another. It doesn't run it. Interpretation runs it. (Sometimes, when an interpreter is implemented in hardware, we call it a "CPU", but it's still an interpreter.)
Case in point: every single currently existing mainstream JavaScript implementation has a compiler.
V8 started out as a pure compiler: it compiled JavaScript straight to moderately optimized native machine code. Later, a second compiler was added. Now, there are two compilers: a lightweight compiler that produces moderately optimized code but the compiler itself is very fast and uses little RAM. This compiler also injects profiling code into the compiled code. The second compiler is a more heavyweight, slower, more expensive compiler, which, however, produces much tighter, much faster code. It also uses the results of the profiling code injected by the first compiler to make dynamic optimization decisions. Also, the decision which code to re-compile using the second compiler is made based on that profiling information. Note that at no time there is an interpreter involved. V8 never interprets, it always compiles. It doesn't even contain an interpreter. (Actually, I believe nowadays it does, I am describing the first two iterations.)
SpiderMonkey compiles JavaScript to SpiderMonkey bytecode, which it then interprets. The interpreter also profiles the code, and then the code which gets executed most often is compiled by a compiler to native machine code. So, SpiderMonkey contains two compilers: one from JavaScript to SpiderMonkey bytecode, and another from SpiderMonkey bytecode to native machine code.
Almost all JavaScript execution engines (with the exception of V8) follow this model of an AOT compiler that compiles JavaScript to bytecode, and a mixed-mode engine that switches between interpreting and compiling that bytecode.
You wrote in a comment:
I really was thinking that machine code is somewhere involved.
What does "machine code" even mean?
What is one man's machine language is another man's intermediate language and vice versa? For example, there are CPUs which can natively execute JVM bytecode, on such a CPU, JVM bytecode is native machine code. And there are interpreters for x86 machine code, when you run of those x86 machine code is interpreted bytecode.
There is an x86 interpreter called JPC written in Java. If I run x86 machine code on JPC running on a native JVM CPU … which is the bytecode and which is the native code? If I compile x86 machine code to JavaScript (yes, there are tools which can do that) and run it in a browser on my phone (which has an ARM CPU), which is the bytecode and which is the native machine code? What if the program I am compiling is a SPARC emulator, and I use it to run SPARC code?
Note that every language induces an abstract machine, and is machine language for that machine. So, every language (including very high-level languages) is native machine code. Also, you can write an interpreter for every language. So, every language (including x86 machine code) is not-native.