Today we’re excited to announce the release of TypeScript 2.4!
If you haven’t yet heard of TypeScript, it’s a superset of JavaScript that brings static types and powerful tooling to JavaScript. These static types are entirely optional and get erased away – you can gradually introduce them to your existing JavaScript code, and get around to adding them when you really need. At the same time, you can use them aggressively to your advantage to catch painful bugs, focus on more important tests that don’t have to do with types, and get a complete editing experience. In the end, you can run TypeScript code through the compiler to get clean readable JavaScript. That includes ECMAScript 3, 5, 2015, and so on.
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
Visual Studio 2015 users (who have Update 3) will be able to get TypeScript by simply installing it from here. Visual Studio 2017 users using Update 2 will be able to ge TypeScript 2.4 from this installer.
Built-in support for 2.4 should be coming to other editors very soon, but you can configure Visual Studio Code and our Sublime Text plugin to pick up any other version you need.
While our What’s New in TypeScript page as well as our 2.4 RC blog post may be a little more in-depth, let’s go over what’s here in TypeScript 2.4.
Dynamic import()
expressions
Dynamic import
expressions are a new feature in ECMAScript that allows you to asynchronously request a module at any arbitrary point in your program. These modules come back as Promise
s of the module itself, and can be await
-ed in an async function, or can be given a callback with .then
.
What this means in short that you can conditionally and lazily import other modules and libraries to make your application more efficient and resource-conscious. For example, here’s an async
function that only imports a utility library when it’s needed:
async function getZipFile(name: string, files: File[]): Promise<File> {
const zipUtil = await import('./utils/create-zip-file');
const zipContents = await zipUtil.getAsBlob(files);
return new File(zipContents, name);
}
Many bundlers have support for automatically splitting output bundles (a.k.a. “code splitting”) based on these import()
expressions, so consider using this new feature with the esnext
module target. Note that this feature won’t work with the es2015
module target, since the feature is anticipated for ES2018 or later.
String enums
TypeScript has had string literal types for quite some time now, and enums since its release. Having had some time to see how these features were being used, we revisited enums for TypeScript 2.4 to see how they could work together. This release of TypeScript now allows enum members to contain string initializers.
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
String enums have the benefit that they’re much easier to debug with, and can also describe existing systems that use strings. Like numeric enums and string literal types, these enums can be used as tags in discriminated unions as well.
enum ShapeKind {
Circle = "circle",
Square = "square"
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
type Shape = Circle | Square;
Improved checking for generics
TypeScript 2.4 has improvements in how types are inferred when generics come into play, as well as improved checking when relating two generic function types.
Return types as inference targets
One such improvement is that TypeScript now can let types flow through return types in some contexts. This means you can decide more freely where to put your types. For example:
function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {
return a => a.map(f);
}
const lengths: (a: string[]) => number[] = arrayMap(s => s.length);
it used to be the case that s
would need to be explicitly annotated or its type would be inferred as {}
. While lengths
could be left unannotated in that case, it felt surprising to some users that information from that type wasn’t used to infer the type of s
.
In TypeScript 2.4, the type system knows s
is a string
from the type of lengths
, which could better fit your stylistic choices.
This also means that some errors will be caught, since TypeScript can find better candidates than the default {}
type (which is often too permissive).
let x: Promise<string> = new Promise(resolve => {
resolve(10);
// ~~ Now correctly errors!
});
Stricter checking for generic functions
TypeScript now tries to unify type parameters when comparing two single-signature types. As a result, you’ll get stricter checks when relating two generic signatures which may catch some bugs.
type A = <T, U>(x: T, y: U) => [T, U];
type B = <S>(x: S, y: S) => [S, S];
function f(a: A, b: B) {
a = b; // Error
b = a; // Ok
}
As a temporary workaround for any breakage, you may be able to suppress some of these errors using the new --noStrictGenericChecks
flag.
Strict contravariance for callback parameters
TypeScript has always compared parameters in a bivariant way. There are a number of reasons for this, and for the most part it didn’t appear to be a major issue until we heard more from users about the adverse effects it had with Promise
s and Observable
s. Relating two Promise
s or Observable
s should use the type arguments in a strictly covariant manner – a Promise<T>
can only be related to a Promise<U>
if T
is relatable to U
. However, because of parameter bivariance, along with the structural nature of TypeScript, this was previously not the case.
TypeScript 2.4 now tightens up how it checks two function types by enforcing the correct directionality on callback parameter type checks. For example:
interface Mappable<T> {
map<U>(f: (x: T) => U): Mappable<U>;
}
declare let a: Mappable<number>;
declare let b: Mappable<string | number>;
a = b; // should fail, now does.
b = a; // should succeed, continues to do so.
In other words, TypeScript now catches the above bug, and since Mappable
is really just a simplified version of Promise
or Observable
, you’ll see similar behavior with them too.
Note that this may be a breaking change for some, but this more correct behavior will benefit the vast majority of users in the long run.
Stricter checks on “weak types”
TypeScript 2.4 introduces the concept of “weak types”. A weak type is any type that contains nothing but all-optional properties. 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. That includes primitives like number
, string
, and boolean
.
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'.
This check also catches situations like classes that might forget to implement members of an interface:
interface Foo {
someMethod?(): void;
someOtherMethod?(arg: number): string;
}
// Error! Did 'Dog' really need to implement 'Foo'?
class Dog implements Foo {
bark() {
return "woof!";
}
}
This change to the type system may introduce some breakages, but in our exploration of existing codebases, this new check primarily catches silent errors that users weren’t aware of.
If you really are sure that a value should be compatible with a weak type, consider the following options:
- Declare properties in the weak type that are always expected to be present.
- Add an index signature to the weak type (i.e.
[propName: string]: {}
). - Use a type assertion (i.e.
opts as Options
).
In the case above where the class Dog
tried to implement Foo
, it’s possible that Foo
was being used to ensure code was implemented correctly later on in a derived class. You can get around this by performing class-interface merging.
class Dog {
bark() {
return "woof!";
}
}
interface Dog implements Foo {
// 'Dog' now inherits all of the methods from 'Foo'.
}
Enjoy!
You can read up our full what’s new in TypeScript page on our wiki for some more details on this new release. To also see a full list of breaking changes, you can look at our breaking changes page as well.
Keep in mind that any sort of constructive feedback that you can give us is always appreciated, and used as the basis of every new version of TypeScript. Any issues you run into, or ideas that you think would be helpful for the greater TypeScript community can be filed on our GitHub issue tracker.
If you’re enjoying TypeScript 2.4, let us know on Twitter with the #iHeartTypeScript hashtag on Twitter.
Thanks for reading up on this release, and happy hacking!
Dynamic imports will be used immediately. Fantastic work TypeScript team.
Yep I hope it will be used in t***_web.
Great job! <3
There used to be a time I used to manage like a hawk all the places where TypeScript would silently fail to give an error. I just trust the compiler to catch most of them now and it hasn’t let me down. Thank you! 🌹
string enums <3
When will we have elvis operator?
Reusing my response below to LionSoft – this is pending on the progress of the “Null Propagation” proposal in TC39. When it reaches a mature-enough stage, we’ll be able to commit to support in TypeScript.
When are you planning to add the ‘elvis’ operator (obj?.prop)???
+1
Currently this is pending on the progress of the “Null Propagation” proposal in TC39. When it reaches a mature-enough stage, we’ll be able to commit to support in TypeScript.
What i suggest is use http://bridge.net/ – it supports C# 6 which supports your elvis operator –
bridge.net converts to javascript 🙂 – you can even use visual studio
How do you point Visual Studio to the new 2.4 typescript SDK install. My path points to it but VS is still using the old SDK.
correction–VS is picking up the tsc.exe from the path but intellisence is not up with the new features.
O.
Tools –> Options –> Text Editor –> JavaScript/TypeScript –> Intellisense –> General –> Use TypeScript version: x.x
Fantastic! Thanks.
BNalo
i do hate javascript with all my soul and physical existance. but do i getting hyped for typescript i will probably study on this. thanks typescript team.
ur an idiot
Hey. try Bridge.net – uses C# for types to javascript – and they just added in there beta version – debugging in c# mapped files via Chrome.
You probably won’t succeed in TypeScript if you don’t know JavaScript. Most of the quirky things of JavaScript still exist in TypeScript. Good luck!
Are there any plans to add partial classes, or some other way to break up a class amongst a set of files?
At this time we don’t have plans for this. You can, however, use module augmentations to perform class/interface merging to re-open the type of a class, and then add methods directly to the class’ prototype.
What i suggest is move over to http://bridge.net/ – uses c# 6 – beta support for c# 7,
and it converts it to debug able javascript
Contravariance was the thing I was hoping would happen one day. That day is now. Keep being excellent, TypeScript!
Nice…still no reverse-mapping for string enums?
If there are any c# programmers and want to move over to javascript and don’t want to use typescript:
try bridge.net – c# to javascript.
shameless advertising…
Holy shit man, do you get paid by the post? Even if I didn’t think that one was just asking for headaches using a transpiler like that, I’d never consider it simply because of your incessant advertising it.
I wonder could you all have a look at this Stack Overflow question and expand on the exact nature of the problem? I referred the asker to this blog post but I confess I too don’t see why 2.4 regards his types as incompatible.
Your filters are a bit aggressive and I’m having trouble posting a link to Stack Overflow, you’ll have to add the protocol yourselves! stackoverflow.com/questions/44917558
Brilliant work yet again guys.
What about TypeScript 2.4 for Visual Studio 2017 15.3 Preview 3, I can no longer see menu for choosing TS version at all in VS options(TypeScript => Intelisense)
I have Visual Studio 2017.2 (26430.15), but my TypeScript Version is still 2.2.2.0. In the past, Visual Studio updates regularly came with new TypeScript versions. Do you know whether they stopped shipping new TypeScript Versions together with Visual Studio updates?
Any way to compile Typescript straight into .NET assemblies without losing type information? IOW, is there any existing support or planned support for making Typescript a bonafide .NET language? I’m looking to use Typescript with Unity3d, so need to target .NET Mono platform.
e of TypeScript 2.4!
If you haven’t yet heard of TypeScript, it’s a superset of JavaScript that brings static types and powerful tooling to JavaScript. These static types are entirely optional and get erased away – you can gradually introduce them to your existing JavaScript code, and get around to adding them when you really need. At the same time, you can use them aggressively to your advantage to catch painful bugs, focus on more important tests that don’t have to do with types, and get a complete editing experience. In the end, you can run TypeScript code through the compiler to get clean readable JavaScript. That includes ECMAScript 3, 5, 2015, and so on.
agario unblocked