Skip to main content

2 posts tagged with "es2015"

View All Tags

The Ternary Operator <3 Destructuring

I'm addicted to the ternary operator. For reasons I can't explain, I cannot get enough of:

const thisOrThat = (someCondition) ? "this" : "or that"

The occasion regularly arises where I need to turn my lovely terse code into an if statement in order to set 2 variables instead of 1. I've been heartbroken; I hate doing:

let legWear: string, coat: boolean;
if (weather === "good") {
legWear = "shorts";
coat = false;
}
else {
legWear = "jeans";
coat = true;
}

Just going from setting one variable to setting two has been really traumatic:

  • I've had do stop using const and moved to let. This has made my code less "truthful" in the sense that I never intend to reassign these variables again; they are intended to be immutable.
  • I've gone from 1 line of code to 9 lines of code. That's 9x the code for increasing the number of variables in play by 1. That's... heavy.
  • This third point only applies if you're using TypeScript (and I am): I have to specify the types of my variables up front if I want type safety.

ES2015 gives us another option. We can move back to the ternary operator if we change the return type of each branch to be an object sharing the same signature. Then, using destructuring, we can pull out those object properties into consts:

const { legWear, coat } = (weather === "good")
? { legWear: "shorts", coat: false }
: { legWear: "jeans", coat: true }

With this approach we're keeping usage of const instead of let and we're only marginally increasing the amount of code we're writing. If you're using TypeScript you're back to being able to rely on the compiler correctly inferring your types; you don't need to specify. Awesome.

Crowdfund You A Tuple#

I thought I was done and then I saw this:

@johnny_reilly even neater with tuples: const [str, num] = test ? ["yes", 100] : ["no", 50];

โ€” Illustrated Pamphlet (@Rickenhacker) August 20, 2016

Daniel helpfully points out that there's an even terser syntax available to us:

const [ legWear, coat ] = (weather === "good")
? [ "shorts", false ]
: [ "jeans", true ]

The above is ES2015 array destructuring. We get exactly the same effect but it's a little terser as we don't have to repeat the prop names as we do when using object destructuring. From a TypeScript perspective the assignment side of the above is a Tuple which allows our type inference to flow through in the manner we'd hope.

Lovely. Thanks!

Creating an ES2015 Map from an Array in TypeScript

I'm a great lover of ES2015's <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">Map</a>. However, just recently I tumbled over something I find a touch inconvenient about how you initialise a new Map from the contents of an Array in TypeScript.

This Doesn't Work#

We're going try to something like this: (pilfered from the MDN docs)

var kvArray = [["key1", "value1"], ["key2", "value2"]];
// Use the regular Map constructor to transform a 2D key-value Array into a map
var myMap = new Map(kvArray);

Simple enough right? Well I'd rather assumed that I should be able to do something like this in TypeScript:

const iAmAnArray [
{ value: "value1", text: "hello" }
{ value: "value2", text: "map" }
];
const iAmAMap = new Map<string, string>(
iAmAnArray.map(x => [x.value, x.text])
);

However, to my surprise this errored out with:

[ts] Argument of type 'string[][]' is not assignable to parameter of type 'Iterable<[string, string]>'.
Types of property '[Symbol.iterator]' are incompatible.
Type '() => IterableIterator<string[]>' is not assignable to type '() => Iterator<[string, string]>'.
Type 'IterableIterator<string[]>' is not assignable to type 'Iterator<[string, string]>'.
Types of property 'next' are incompatible.
Type '(value?: any) => IteratorResult<string[]>' is not assignable to type '(value?: any) => IteratorResult<[string, string]>'.
Type 'IteratorResult<string[]>' is not assignable to type 'IteratorResult<[string, string]>'.
Type 'string[]' is not assignable to type '[string, string]'.
Property '0' is missing in type 'string[]'.

Disappointing right? It's expecting Iterable&lt;[string, string]&gt; and an Array with 2 elements that are strings is not inferred to be that.

This Does#

It emerges that there is a way to do this though; you just need to give the compiler a clue. You need to include a type assertion of as [string, string] which tells the compiler that what you've just declared is a Tuple of string and string. (Please note that [string, string] corresponds to the types of the Key and Value of your Map and should be set accordingly.)

So a working version of the code looks like this:

const iAmAnArray [
{ value: "value1", text: "hello" }
{ value: "value2", text: "map" }
];
const iAmAMap = new Map<string, string>(
iAmAnArray.map(x => [x.value, x.text] as [string, string])
);

Or, to be terser, this:

const iAmAnArray [
{ value: "value1", text: "hello" }
{ value: "value2", text: "map" }
];
const iAmAMap = new Map( // Look Ma! No type annotations
iAmAnArray.map(x => [x.value, x.text] as [string, string])
);

I've raised this as an issue with the TypeScript team; you can find details here.