Today we’re releasing our RC of TypeScript 2.4. To get started with the latest stable version of TypeScript, you can grab it through NuGet, or use the following command with npm:
npm install -g typescript@rc
Visual Studio 2015 users (who have Update 3) can install TypeScript from here, and Visual Studio 2017 users using Update 2 will be able to get TypeScript by simply installing it from here.
To get it working elsewhere, you can easily configure Visual Studio Code and our Sublime Text plugin to pick up whatever version you need. Other editors may have different approaches to swapping TypeScript in.
Dynamic Import Expressions
TypeScript 2.4 is bringing support for ECMAScript’s new import() calls. These calls import a module and return a promise to that module.
For example, imagine a webpage that allows you to create and edit images. When you’re working with one file, the page will allow you to download that file immediately; but if you’re working on multiple images, you can save all of them as a .zip file.
You might have a utility library to create a zip file, but since downloading multiple files isn’t that common, you’d want to load that functionality lazily. import() expressions let you load a module on the fly as a Promise like so:
async function getZipFile(name: string, files: File[]): Promise<File> {
const zipUtil = await import('./utils/create-zip-file');
const zipContents = await zipUtil.getContentAsBlob(files);
return new File(zipContents, name);
}
This feature is so incredibly useful in situations where you want to conditionally import modules. Projects that use bundlers like Webpack can operate on these import() calls and split code into smaller bundles that can be lazily loaded.
What all of this means in the end is
- you can send less JS over the wire for more common scenarios
- your users can get faster page load times for critical content
Safer callback parameter checking
When checking whether two functions are assignable to one another, TypeScript checks whether their parameters are bidirectionally assignable. We call this function parameter bivariance. There are a number of reasons for this, but it mainly stems from TypeScript’s structural nature and how we’ve tried to match people’s intuition.
Our experience has been that users generally don’t run into issues with this all that much; however, we did start to see that this model broke down on containers which handed internal data off through callbacks – specifically, Promises. For example, a Promise<Animal> was assignable to Promise<Dog>, which is incorrect. You can see similar behavior in the following sample:
interface Animal { animalStuff: any }
interface Dog extends Animal { bark(): void }
interface BasicCollection<T> {
forEach(callback: (value: T) => void): void;
}
declare let animalCollection: BasicCollection<Animal>;
declare let dogCollection: BasicCollection<Dog>;
// This should be an error, but TypeScript 2.3 and below allow it.
dogCollection = animalCollection;
To solve this issue, TypeScript 2.4 now tightens things up and compares the parameters which are callbacks specially. When checking callbacks, TypeScript will be strict about checking parameters contravariantly with respect to the current check. Parameter bivariance still applies otherwise, but we found this to be an effective safety check without drastically changing the type system.
Since this may be a bit technical, you can read more on the original pull request. The short story is that you’ll see much better checking when using Promises, Observables, and anywhere else you’ve been using callbacks.
Be aware though – this new check can introduce new type-checking errors in existing codebases. For more details, see our Breaking Changes section.
Weak types
Back in TypeScript 1.6, we added a check for excess properties in object literals. This check looked for unexpected properties in object literals, and it happened to catch a large class of bugs. The only shortcoming of the check was that if you didn’t immediately pass your object literal to something of the appropriate type, the check wouldn’t be triggered.
In TypeScript 2.4, we’re adding a similar check for what we call weak types. Any type that contains only optional properties is considered a weak type since it provides few restrictions on what can be assigned to it. For example, this Options type is a weak type:
interface Options {
data?: string,
timeout?: number,
maxRetries?: number,
}
In TypeScript 2.4, it’s now an error to assign anything to a weak type when there’s no overlap in properties. For example:
function sendMessage(options: Options) {
// ...
}
const opts = {
payload: "hello world!",
retryOnFail: true,
}
// Error!
sendMessage(opts);
// No overlap between the type of 'opts' and 'Options' itself.
// Maybe we meant to use 'data'/'maxRetries' instead of 'payload'/'retryOnFail'.
You can think of this as TypeScript “toughening up” the weak guarantees of these types to catch what would otherwise be silent bugs.
Since this is a breaking change, you may need to know about the workarounds which are the same as those for strict object literal checks:
- Declare the properties if they really do exist.
- Add an index signature to the weak type (i.e.
[propName: string]: {}). - Use a type assertion (i.e.
opts as Options).
String enums
Enums were an early feature TypeScript provided that have been handy for marking a set of well-known related numeric values.
enum E {
A = 1,
B = 2,
C = 3,
}
In TypeScript 1.8, we also released string literal types which could be used in a union.
function setStatus(status: "ready" | "running" | "finished") {
// ...
}
setStatus("ready");
setStatus("stopped");
// ~~~~~~~~~
// Error: Type
//
// '"stopped"'
//
// is not assignable to type
//
// '"ready" | "running" | "finished"'.
There were benefits to both constructs, but each group of users wanted some functionality from the other.
For example, string literal types serialize very well over transfer protocols and during debugging – after all, they’re just strings. But this isn’t the case for numeric enums, which end up as plain numbers without obvious meaning.
But string literal unions can be cumbersome for more advanced scenarios. If you want a well-known set of constants for each string, then you typically have to declare them yourself.
That’s why for TypeScript 2.4, we’re introducing string enums!
enum ActionType {
AddUser = "ADD_USER",
DeleteUser = "DELETE_USER",
RenameUser = "RENAME_USER",
// Aliases
RemoveUser = DeleteUser,
}
String enums have a simple representation, but come with the caveat that they don’t create a reverse-mapping. In other words, you can’t index into ActionType with the string "ADD_USER" (by writing ActionType["ADD_USER"]) to get the name of AddUser.
Have fun!
You can see the full list of what’s new in 2.4 here on our wiki.
Then give TypeScript 2.4 RC a try and let us know what you think! If you’re having an awesome experience, use the #iHeartTypeScript hashtag on Twitter and let us know.
If you run into any places you think we could improve, we’re always all-ears on suggestions.

s/well-knwon/well-known
s/with obvious meaning/without obvious meaning
Fixed, thanks!
Love these new features: dynamic imports, weak types, string enums and better callback checks. Great work team 🌹
🌹
I am psyched for dynamic imports! You guys are killing it. But I have to admit, I feel like I am still playing catch up from everything that’s been introduced since 1.8.
Our IT architecture didn’t approve TypeScript for general use in our company. They cited the high likelihood of unsupported TypeScript code in the next 2 – 3 years without significant rework upgrading to the next version plus the many man-months retesting an upgrade.
I like the direction in pushing JS; consider pushing the ECMA JS standard more.
Are any JavaScript based tools or framework allowed? In my opinion, TypeScript is one of the safer bets in the JavaScript environment as Microsoft relies on it for its other products as well (VS Code, Office Add-In Development and SharePoint Framework are all based on TypeScript – and I guess there is a ton of internal development that uses it as well). If Angular, React and other frameworks are still “cool” and used in three years is a complete open topic…
If they allow you JavaScript, then banning TypeScript is nonsense – you can throw away TS files in any point of time and stay with transpilled JS files and continue your work. Produced JS is very close to what you’d have written by hand. So the risk of rewrite, if you “like” JS is exactly zero. Your IT architect is probably incompetent… It is like rejecting ReShaper beacuse “it may be instable or badly supported in next few years”.
The productivity boost by features like “rename” or “intelisense” and early bug elimination is tremendous by any type-checking tool, especially TS.
If only VS would have cool features like “rename” or “intellisense”… 😉
Google has accepted it as one of their 6 languages they allow, If they allow it, then I would think everyone should.
Where is ?. operator, I need it all the time…😞
It’s there, I’m using Typescript version 2.3.2 and it’s there
Are you sure? They never announced that its even been bucketed into a version yet.
TypeScript is committed to be a super-set of JavaScript. that means TypeScript needs to follow the same semantics of JavaScript for features like safe navigation operator. Currently this is waiting on discussions in TC39 about the feature semantics, once it advances in the standardization committee TypeScript will add support for it. Issue tracking the feature is https://github.com/Microsoft/TypeScript/issues/16.
I think maybe you are using Angular? From what I understand the Angular compiler parses it and figures it out for itself https://stackoverflow.com/questions/37126423/angular-2-templates-safe-navigation-operator-for-methods
I thought, that after async for es3/5 there’s little more I need from TS. But the dynamic imports and the string enums.. gentlemen, I tip my hat to you all. Great job.
Is there a synchronous import?
Sometimes I don’t need an asynchronous import.
Webpack has require function which I use now but some standard solution would be better.
Thank you for your hard work.
Dynamic imports is one of the most expected feature for me.
I need to use it with webpack’s require.ensure code splitting feature.
Could it be possible or is there any work-around to use “dynamic import with “require.ensure”?
I didn’t notice that webpack already have import function supported.
https://webpack.js.org/guides/code-splitting-async/#dynamic-import-import-
Any work on using something like:
“ready” | “running” | “finished” | string
That way, you could have intellisense for the first three and leave the option to use any other string as assignable. Today that ends up on killing all and leave just string.
Do I have to use Webpack for dynamic imports to have any effect on bundle size? Currently using requirejs and grunt-msbuild.
In case this helps anyone…
I installed the 2.4 RC for Visual Studio 2017 and created a new web application and TypeScript file with the code in this blog post that is now a compilation error, but found the JavaScript file was still being generated without a compilation error. After a lot of trial and error I found that to see the compilation error in the output window and error list window, you have to invoke the build command in the Visual Studio project which the TypeScript file is included in. This occurs when having the “compileOnSave”:true setting in tsconfig.json, and saving the file will still get compiled and will not show any kind of error in the status bar or output window.
Here’s my sample tsconfig.json file for reference:
{
“compilerOptions”: {
“noImplicitAny”: false,
“noEmitOnError”: true,
“removeComments”: false,
“sourceMap”: true,
“target”: “es5”,
“outFile”: “output.js”
},
“compileOnSave”: true,
“exclude”: [
“node_modules”,
“wwwroot”
]
}
Hi Alan, this was probably a result of the Visual Studio language service using a different version of TS than the build. If you’re using Visual Studio 2017 15.2, you’ll want to change your language service version using the setting described here: https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes#whats-new-in-this-release . If you’re using Visual Studio 2017 15.3, the TypeScript version is settable through your project properties instead.
@Mark, that setting does the trick, thanks! Using Visual Studio 2017 15.2, and setting the IntelliSense Typescript version to 2.4, does start to make the error appear in the Error List window after saving and before building. Hopefully in the future Visual Studio will default to using the newest installed version TypeScript like MSBuild does.
Thanks – this was what I needed to sensibly fix my errors (99% resulting from still having used JQueryPromise and it not quite lining with PromiseLike). VS is *very* slow after upgrading though 🙁 Hoping it’s just whilst I fix these errors.
Great work team, and thanks for the string enums 🙂
Can string enums be const enums?
Can anyone explain how to use with VS2017.3?
Nice…but why there is no reverse-mapping for string enums?… it is very essential.