Lets Talk about ...

Whats new (and upcoming) in JS

2020 Edition

Stages

Stage 0 (Strawperson)

Stage 1 (Proposal)

Already mentioned

Dedent

Current

console.log(`
  query foo {
    bar
  }
`)

 query foo {
   bar
 }


Expected

console.log(`
  query foo {
    bar
  }
`)
query foo {
  bar
}

Current Solution

import dedent from 'ts-dedent'

console.log(dedent`
  query foo {
    bar
  }
`)
query foo {
  bar
}

Problems

const query = dedent(sql)`
  select *
  from students
  where name = ${name}
`
const raw = dedent`
  select *
  from students
  where name = ${name}
`

sql`${raw}`;

Proposal

console.log(```
  query foo {
    bar
  }
```)
query foo {
  bar
}

UUID

UUID

randomUUID();
// => "52e6953d-edbe-4953-be2e-65ed3836b2f0"

Async Constructors

Async Constructors

class X {
  async constructor() {
     await ...;
  }
}
class Y extends X {
  async constructor() {
     await.super();
  }
}

Number.range

Number.range

Number.range(0, Infinity)
  .take(1000)
  .filter((x) => !(x % 3))
  .toArray()
[...Number.range(1, 100, 2)]
// => odd number from 1 to 99

BigInt.range

BigInt.range(0, Infinity)
  .take(1000)
  .filter((x) => !(x % 3))
  .toArray()
[...BigInt.range(1, 100, 2)]
// => odd number from 1 to 99

Array

Array.equals

[{
  foo: 'bar'
}, {
  foo: 'baz'
}].equals[{
    foo: 'bar'
}] // => false
[{ foo: 'bar' }, { foo: 'baz' }].equals([
  { foo: 'bar' }, { foo: 'baz' }
]) // => true

Array.uniqueBy

[1, 2, 3, 3, 2, 1].uniqueBy();
// => [1, 2, 3]

Array.uniqueBy

data = [
  { id: 1, uid: 10000 },
  { id: 2, uid: 10000 },
  { id: 3, uid: 10001 }
];
data.uniqueBy('uid');
// [
//   { id: 1, uid: 10000 },
//   { id: 3, uid: 10001 }
// ]

Array.uniqueBy

data.uniqueBy(({ id, uid }) => `${id}-${uid}`);
// [
//   { id: 1, uid: 10000 },
//   { id: 2, uid: 10000 },
//   { id: 3, uid: 10001 }
// ]

Array.item

[1, 2, 3].item(1);
// => 2
[1, 2, 3].item(-1);
// => 3

await.ops

await.ops

// before
await Promise.all(
  users.map(async x => fetchProfile(x.id))
)
// after
await.all users.map(
  async x => fetchProfile(x.id)
)

await.ops

await.all expr
// => await Promise.all(expr)
await.race expr
// => await Promise.race(expr)
await.allSettled expr
// => await Promise.allSettled(expr)
await.any expr
// => await Promise.any(expr)

Stage 2 (Draft)

Stage 3 (Candidate)

Already mentioned

private

in classes or objects

class ColorFinder {
  static #red = "#ff0000";
  static #blue = "#00ff00";
  static #green = "#0000ff";
class ColorFinder {
  static #red = "#ff0000";
  static #blue = "#00ff00";
  static #green = "#0000ff";
  static colorName(name) {
    switch (name) {
      case "red": return ColorFinder.#red;
      case "blue": return ColorFinder.#blue;
      case "green": return ColorFinder.#green;
      default: throw new RangeError("unknown color");
    }
  }
}

Record

Record & Tuple

Record

#{ a: 1 }

Record

assert({ a: 1 } !== { a: 1 });
assert(Object(#{ a: 1 }) !== Object(#{ a: 1 }));
assert(#{ a: 1 } === #{ a: 1 });
assert(#{ a: NaN } === #{ a: NaN });
assert(typeof #{ a: 1 } === "record");
assert(#{ a: 1 } === JSON.parseImmutable(
  JSON.stringify(#{ a: 1 })
));

Record

assert(#{ a: 1, b: 2 } === #{ b: 2, a: 1 });
Object.keys(#{ a: 1, b: 2 }) // => ["a", "b"]
Object.keys(#{ b: 2, a: 1 }) // => ["a", "b"]

Tuple

#[ 1, 2, 3 ]

Tuple

assert([1, 2] !== [1, 2]);
assert(Object(#[1, 2]) !== Object(#[1, 2]));
assert(#[1, 2] === #[1, 2]);
assert(#[NaN] === #[NaN]);
assert(typeof #[1, 2] === "tuple");
assert(#[1, 2] === JSON.parseImmutable(
  JSON.stringify(#[1, 2])
));

Import Assertions

Import Assertions

import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } });

Import Assertions

using TypeScript / Babel

import * as data from "./foo.json";
// "compilerOptions": { "resolveJsonModule": true }

Optional

Chaining

Current

var query = 'input[name=foo]';
var $input = $form.querySelector(query)
var value = $input ? $input.value : undefined

With optional chaining

var fooValue = $form.querySelector(query)?.value
var street = user?.address?.street
iterator.return?.() // manually close an iterator

Nullish

Coalescing

current

const undefinedValue =
  response.settings?.undefinedValue
  || 'some other default'
; // result: 'some other default'
const nullValue =
  response.settings?.nullValue
  || 'some other default'
; // result: 'some other default'

current problems

response.settings?.showSplashScreen || true
// False evaluates to false, result: true
response.settings?.animationDuration || 300;
// 0 evaluates to false, result: 300
response.settings?.headerText || 'Hello, world!'
// '' evaluates to false, result: 'Hello, world!'

Nullish Coalescing

response.settings?.showSplashScreen ?? true
response.settings?.animationDuration ?? 300;
response.settings?.headerText ?? 'Hello, world!'

Numeric

Separators

current

1000000000
101475938.38
// Is this a billion? a hundred millions? Ten millions?
let fee = 12300;
// is this 12,300? Or 123, because it's in cents?
let amount = 1234500;
// is this 1,234,500? Or cents, hence 12,345?
// Or financial, 4-fixed 123.45?

Numeric Separators

1_000_000_000           // Ah, so a billion
101_475_938.38          // hundreds of millions
let fee = 123_00;       // $123 (12300 cents)
let fee = 12_300;       // $12,300 (thats much)
let amount = 12345_00;  // 12,345 (1234500 cents)
let amount = 123_4500;  // 123.45 (4-fixed fin.)
let amount = 1_234_500; // 1,234,500

Numeric Separators

0.000_001 // fractions
1e10_000  // exponents
let nibbles = 0b1010_0001_1000_0101; // binary
let brandColor = 0x44_BB_44; // hex (colors)

Top Level await

const connection = await dbConnector();
const strings = await import(`/i18n/${navigator.language}`);

 

Thats it

Whats next?

Questions?