Need clarification of the target and lib compiler options

I find I'm confused by the target and lib options and how they interact with the features supported in the source code. I feel the docs need improving a little so am asking here before raising an issue.

I naively assumed that target specifies the version of JS that the output code requires to run (with the addition of a module loader). Thus we can always use all the advanced JS features (like object spread) that TS supports in our source and the compiler generates suitable code for the target we specify. I assume it had polyfills etc at hand and the code would just run on the target VM.

However the docs for the lib option specify the default libs depend on the target. But, libs effect what source types are available and so effect what code we can use. Thus the source features we can use depend on the target. That is not as I expected. I should say my understanding of lib is that they are typings with a different API, though the docs do not really say what they are.

I can see that here are some language features that do not depend on types and others that do. However it's not clear if that's part of the reason for this situation.

Can someone please clarify this?

A secondary question is why is there both an ES6 and an ES2015 lib when they are usual documented as being the same thing.

thanks


(This started as a comment but it got too long.)

It's a bit confusing partly because there's some history behind it. I'm not qualified to answer this authoritatively but I've been following TypeScript since early development and this is my understanding:

  • --target tells the compiler what library version to include while compiling (for example ES5 will give a compiler error if you use Promise, but ES6 will know all about Promise) and what version of JS is emitted by the compiler (for example ES5 will down-compile class syntax, but ES6 will leave it in).
  • --lib was added later to give you better control over what library version to use while compiling without changing the emitted JS target. For example, a common problem was that you may include polyfills for ES6 library features, such as Promise, but you want to target ES5 browsers by down-compiling class syntax. Before --lib was around you either had to target ES6 to avoid compile errors about Promise, then down-compile again using Babel, or you could target ES5 and provide your own type definition for Promise so that the compiler doesn't give you an error. Now with --lib you can simply say your --target ES5 and --lib ES6, and the compiler will not complain about Promise but still down-compile to ES5.
  • Neither option will cause TS to emit any library polyfills (Promise, etc), as you evidently found out; it's your responsibility to provide the correct runtime libraries. It only emits a few down-level language compatibility helpers, like __extends and __awaiter (the difference being that class or async is not just an API that can be polyfilled at runtime, it's a language feature with syntax implications). The --lib option is just your way of getting the right level of compile checking based on what you know you are going to have at runtime.
  • As for why there's both ES6 and ES2015, that's just because ECMAScript changed the name and TS left the old name as a valid option for backwards compatibility. :)

You'll find a lot of this covered in these TS issues:

  • #4168 - Normalize our lib files by compiler settings
  • #6974 - Proposal: Modularize Library

TSConfig Reference links:

  • target
  • lib