in ,

The 5 Big Features of TypeScript 3.7 and How to Use Them, Hacker News

The 5 Big Features of TypeScript 3.7 and How to Use Them, Hacker News


The TypeScript 3.7 release is coming soon, and it’s going to be a big one.

The target release date is November 5th, and there’s some seriously exciting headline features included:

  • Assert signatures
  • Recursive type aliases
  • Top-level await
  • Null coalescing

  • Optional chaining

Personally, I’m super excited about this, they’re going to whisk away some annoyances I’ve been fighting against forever!

If you haven’t been paying close attention to the TypeScript development process though, it’s probably not clear what half of these mean, or why you should care. Let’s talk them through.

Assert Signatures

This is a brand-new & little-known TypeScript feature, which allows you to write functions that act liketype guardsas a side-effect, rather than explicitly returning their boolean result.

It’s easiest to demonstrate this with a JavaScript example:

      

functionassertString(input){    if(inputinstanceof'string')return;    elseThrow(new)Error('Input must be a string!');}functiondoSomething(input){    assertString(input);    }doSomething('abc') ();doSomething(123);

      

This pattern is neat and useful and you can’t use it in TypeScript today.

TypeScript can’t know that you’ve guaranteed the type ofinputafter it’s runassertString. Typically people just make the argumentinput: stringto avoid this, and that’s good, but that also just pushes the type checking problem somewhere else, and in cases where you just want to fail hard it’s useful to have this option available.

Fortunately, soon we will:

      

functionassertString(input:any):asserts inputisstring{    if(inputinstanceof'string')return;    elseThrow(new)Error('Input must be a string!');}functiondoSomething(input:stringnumber){    assertString(input);    }

      

Hereassert input is stringmeans that if this function ever returns, TypeScript can narrow the type ofinputtostring, just as if it was inside an if block with a type guard.

To make this safe, that means if the assert statement isn’t true then your assert function must either throw an error or not return at all (kill the process, infinite loop, you name it).

That’s the basics, but this actually lets you pull some really neat tricks:

      

functionassert(input:any):asserts input{    if(!input)thrownewError('Not a truthy value');}declareconstx:(number)|string|undefined;Assert(x);Assert(typeofx==='string');consta:Result|Error=doSomethingTestable();expect(a).is.instanceOf(result);expect(a.resultValue).to.equal()123);functionassertDefinedT>(OBJ:T):asserts OBJisNonNullableT>{    if(OBJ===undefined||obj===null){        ThrownewError('Must not be a nullable value');    }}declareconstx:string|undefined;consty=x!;assertDefined(x);constz=x;typeX(T)extendsstring|{}>={value:(T)};functionsetX(T)   (extends)string|{}>(x:Xany>,v:T):asserts xisXT>{    x.value=V;}declareletx:(X)any>;setX(x,123);

      

This is still in flux, so don’t take it as the definite result, and keep an eye on thepull requestif you want the final details.

There’s evendiscussionthere about allowing functions to assert somethingandreturn a type, which would let you extend the final example above to track a much wider variety of side effects, but we’ll have to wait and see how that plays out.

Top-level Await

Async / awaitis amazing, and makes promises dramatically cleaner to use.

Unfortunately though, you can’t use them at the top level. This might not be something you care about much in a TS library or application, but if you’re writing a runnable script or using TypeScript in aREPLthen this gets super annoying. It’s even worse if you’re used to frontend development, sinceawaithas been legal at the top level in Chrome and Firefox for a couple of years now.

Fortunately though, a fix is ​​coming. This is actually a general stage-3 JSproposal, so it’ll be everywhere else eventually too, but for TS devs 3.7 is where the magic happens.

This one’s simple, but let’s have another quick demo anyway:

      

AsyncfunctiondoEverything(){    ...    constresponse=awaitfetch('http://example.com');    ...}doEverything();

      

With top-level await:

      

...constresponse=awaitfetch('http://example.com');...

      

There’s a notable gotcha here: if you’renotwriting a script, or using a REPL, don’t write this at the top level, unless youreallyknow what you’re doing!

It’s totally possible to use this to write modules that do blocking async steps when imported. That can be useful for some niche cases, but people tend to assume that theirimportstatement is a synchronous, reliable & fairly quick operation, and you could easily hose your codebase’s startup time if you start blocking imports for complex async processes (even worse, processes that can fail).

This is somewhat mitigated by the semantics of imports of async modules: they’re imported and run inparallel, so the importing module effectively waits forPromise.all (importedModules)before being executed. Rich Harris wrotean excellent pieceon a previous version of this spec, before that change, when imports ran sequentially and this problem was much worse), which makes for good background reading on the risks here if you’re interested.

It’s also worth noting that this is only useful for module systems that support asynchronous imports. There isn’t yet a formal spec for how TS will handle this, but that likely means that a very recenttargetconfiguration, and either ES Modules or Webpack v5 (whose alphas haveexperimental support) at runtime.

Recursive Type Aliases

If you’re ever tried to define a recursive type in TypeScript, you may have run into StackOverflow questions like this:https://stackoverflow.com/questions/ 47842266 / recursive-types-in-typescript.

Right now, you can’t. Interfaces can be recursive, but there are limitations to their expressiveness, and type aliases can’t. That means right now, you need to combine the two: define a type alias, and extract the recursive parts of the type into interfaces. It works, but it’s messy, and we can do better.

As a concrete example, this is thesuggestedtype definition for JSON data:

      

typeJSONValue=    |string    |number    |Boolean    |JSONObject     |JSONArray;interfaceJSONObject{    [x:string]:JSONValue;}interfaceJSONArrayextendsArrayJSONValue>{}

      

That works, but the extra interfaces are only there because they’re required to get around the recursion limitation.

Fixing this requires no new syntax, it just removes that restriction, so the below compiles:

      

typeJSONValue=    |string    |number    |Boolean    |{[x:string]:JSONValue}    |ArrayJSONValue>;

      

Right now that fails to compile withType alias 'JSONValue' circularly references itself. Soon though, soon …

Null Coalescing

Aside from being difficult to spell, this one is quite simple & easy. It’s based on a JavaScript stage-3proposal, which means it’ll also be coming to your favorite vanilla JavaScript environment too soon, if it hasn’t already.

In JavaScript, there’s a common pattern for handling default values, and falling back to the first valid result of a defined group. It looks something like this:

      

constresult=firstResult||secondResult;this.configValue=options.  configValue||'default';

      

This is useful in a host of cases, but due to some interesting quirks in JavaScript, it can catch you out. IffirstResultoroptions.configValuecan meaningfully be set tofalse, an empty string or0, then this code has a bug. If those values ​​are set, then when considered as booleans they’refalsy, so the fallback value (secondResult/'default') is used anyway.

Null coalescing fixes this. Instead of the above, you’ll be able to write:

      

constresult=firstResult??secondResult;this.configValue=options.  configValue??'default';

      

??differs from||in that it falls through to the next value only if the first argument is null or undefined, not falsy. That fixes our bug. If you passfalseasfirstResult, that will be used instead ofsecondResult, because while it’s falsy it is still defined, and that’s all that’s required.

Simple, but super useful, and takes a way a whole class of bugs.

Optional Chaining

Last but not least, optional chaining is another stage-3proposalwhich is making its way into TypeScript.

This is designed to solve an issue faced by developers in every language: how do you get data out of a data structure when some or all of it might not be present?

Right now, you might do something like this:

      

letresult=data?(data.  (key1) ?data.key1.key2:undefined):undefined;letresult=(( (data)  ||{}).Key1||{}

).key2;

      

Nasty! This gets much much worse if you need to go deeper, and although the 2nd example works at runtime, it won’t even compile in TypeScript since the first step could be{}, in which case (key1isn’t a valid key at all.

This gets still more complicated if you’re trying to get into an array, or there’s a function call somewhere in this process.

There’s a host of other approaches to this, but they’re all noisy, messy & error-prone. With optional chaining, you can do this:

      

letresult=data?.  key1?.key2; array?.[0]?.['key']; OBJ.method?.();letresult=data?.  key1?.key2??'default';

      

The last case shows how neatly some of these dovetail together: null coalescing optional chaining is a match made in heaven.

One gotcha: this will return undefined for missing values, even if they were null, e.g. in cases like(null) ?. key(returns undefined). A small point, but one to watch out for if you have a lot ofnullin your data structures.


That’s the lot! That should outline all the essentials for these features, but there’s lots of smaller improvements, fixes & editor support improvements coming too, so take a look at theofficial roadmapif you want to get into the nitty gritty .

Hope that’s useful – if you’ve got any questions let me know onTwitter.

While you’re here, if you like JS & want to supercharge your debugging skills, take a look atHTTP Toolkit. One-click HTTP (S) interception & debugging for any JS page, script, or server (plus lots of other tools too).

Brave Browser
(Read More)
Payeer

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

Tesla Channels Elon Musk in Not-so-Humble Model S Brag – CCN.com, Crypto Coins News

Tesla Channels Elon Musk in Not-so-Humble Model S Brag – CCN.com, Crypto Coins News

Can I email…, Hacker News

Can I email…, Hacker News