Maybe its my own ignorance, but language design often doesn't map 1:1, so wouldn't it end up feeling forced?
Maybe its my own ignorance, but language design often doesn't map 1:1, so wouldn't it end up feeling forced?
If there is one thing however that I have learned from using transpilers it is that externs as a whole are a huge waste of time, as it can be difficult to determine their quality upfront. If you are building a Node backend or SPA, you are going to need to rely heavily on existing libraries and will probably find yourself constantly hitting walls while you try to swim against the current of the ecosystem if you try to use a transpiler.
Haxe has a lot of metadata mechanisms that enable mapping language-specific semantics to Haxe standards (@:selfCall, and @:native for example). Haxe also has abstract types, which enable automatic inline runtime conversions (e.g., converting a native type to a standard Haxe type automatically depending on usage in haxe methods).
It's true you can't expect to find a high quality extern for a given version of any given library. However, it's pretty easy to write a small one for the subset of functionality that you require. It always ends up paying off for me.
I agree with what you say about writing your own externs with a subset of the functionality, and I think it is often a better approach than relying on potentially flakey externs. Still I think this sort of workflow is only suited to particular types of projects and especially in a team environment it makes more sense to avoid niche ecosystems.
I think an important question is whether the compiler output will need to be touched by humans. If it's just going to be run or compiled more or whatever then it doesn't really matter how the various features map.
Yes, languages differ and language features rarely map 1:1 onto each other; that's about the other 5 % (as well as platform differences). But a lot of things can be done automatically if you control the conversion process, e.g. converting C#'s extension methods into default methods in Java. Of course, if you don't require the resulting converted code to be worked with, and just need it to run, then you can do a lot of shortcuts. We translate at the source code level because at least the Java code is still maintained and extended on the Java side. Doing it at the IL level would be simpler in some cases, but is impractical for a lot of others.
If behavior of compiled program matches the original one, that is.
Unfortunately, that does make it difficult when your target is JavaScript. Every mature transpiler I've used has had some semantic gotchas that aren't present in the "proper" compiler.
It is possible to compile Swift to JavaScript.
The only thing impeding that is the quality of the project.