What’s New in TypeScript 4.9?. The satisfies operator, auto-accessor… | by Jose Granja | Nov, 2022

Satisfied operators, auto-accessor fields, and more

captured by author

typescript 4.9 The version is planned for release on November 15th. This will be the last year of 2022. It’s been a great year for TypeScript.

In this article, I’ll highlight the most relevant new features. Here is the summary:

  • satisfied operator
  • Support for auto-accessor fields
  • improve checking in operator
  • do not allow comparison NaN

As always, you can follow any example using the available TypeScript playground Here, The best way to learn is always to do it interactively.

satisfied operator

When typing a variable at this point in time, we must decide between defining its type manually or relying on inference. Combination of both is not possible.

Suppose we want to declare a variable where its key can be anything but we want the value to be a number.

If we rely solely on inference we cannot impute anything, but in turn, we know that the keys are a | b | c

// relying on inference to do the typing for us
const foo =
a: 255,
b: 255,
c: 255
;

// typeof foo is
// const foo:
// a: number;
// b: number;
// c: number;
//

Let’s try to implement the key value to be a number:

const foo: Record = 
a: 255,
b: 255,
c: 255
;

// typeof foo is
// const foo: Record

This works, however, we lose the type information of the keys a | b | c, We can easily fix this by providing mapped keys:

type Keys = 'a' | 'b' | 'c';
const foo: Record =
a: 255,
b: 255,
c: 255
;

// typeof foo is
// const foo: Record

Although the above is fine but there is a trade off. If we need any other key then we have to add it to Keys type and foo variable.

So there is no good way to express a restriction without changing the type. So far. with new satisfies operator, we can enforce a type restriction without changing the variable inferred type.

const foo = 
a: 255,
b: 255,
c: 255,
// ❌ We get the error Type 'string' is not assignable to type 'number'
d: 'bar'
satisfies Record;

// ✅ We are not losing any type inferance information
// typeof foo is
// const foo:
// a: number;
// b: number;
// c: number;
// d: string;
//

In the above code, we can see its benefits. we are able to implement that object foo keys are always of type number, We still have to keep the guesswork information about the object.

What about additional properties? Will it fail or pass? There is always a debate going on about this. It’s a bit tricky because you can give a solid argument for or against it.

Final verdict It seems that additional properties will not be allowed which will result in an error at compile time.

type Point = 
x: number,
y: number
;

const origin =
x: 0,
y: 0,
// ❌ Type ' x: number; y: number; z: number; ' does not satisfy
// the expected type 'Point'
z: 0
satisfies Point;

This feature is likely to be developed in the future, making it possible to satisfies Operator used on function declaration.

If you want to know more details or references, you can take a look at the initial offer Here,

ECMAScript decorator is already proposed Stage 3 ,link here) This feature will enable the creation of asset auto-accessors, among other things.

class C 
accessor x = 1;

As part of this release, accessor Field declaration may already be used in TypeScript. It uses the functionality of decorators behind the scenes.

Let’s see how we can use it

class Foo 
accessor a: string = 'a';

const instance = new Foo();

// ✅ it works and prints 'a'
console.log(instance.a);

Note how we can access a Property without any additional boilerplate. JavaScript Output may vary depending on target Output selected. If we use latest Es2022The output will depend on JavaScript private variables released last year.

let’s watch js output code with tsconfig set goals Es2022,

"use strict";
class Foo
constructor()
this.#a_accessor_storage = 'a';

#a_accessor_storage;
get a() return this.#a_accessor_storage;
set a(value) this.#a_accessor_storage = value;

const instance = new Foo();
// ✅ it works and prints 'a'
console.log(instance.a);

The convenience does not end here. we can still make accessors To restrict access inside private class instance or static To provide this without the need for an example.

Let’s look at an example of those two uses:

class Foo 
accessor a: string = 'a';
accessor #b: string = 'b';
static accessor c: string = 'c';

log()
// ✅ it works and prints 'b'
console.log(this.#b)

const instance = new Foo();

// ❌ does not work since property is private
console.log(instance.#b);

// ✅ it works and prints 'c'
console.log(Foo.c);

typescript in There is an elegant way to check exactly what can only exist in a single object from a union.

Let’s look at an example:

type Car = 
wheels: number;
topSpeed: number;

type Plane =
topSpeed: number;

function log(vehicle: Car | Plane)
if ('wheels' in vehicle)
// ✅ vehicle.wheels is of type string
console.log(`vehicle has $vehicle.wheels wheels`)

console.log(`vehicle top topSpeed is $vehicle.topSpeed km/h`)

What happens when the type is not known? before typescript 4.9 It was not possible to type-check it. We will get a TypeScript error.

Now, we can use in operator in conjunction with typeof to reduce the type. If typeof is absent it will be typed unknown,

with example typeof,

function log(vehicle: ) 
// ✅ Works, item.wheels is now of type number
if ('wheels' in vehicle && typeof vehicle.wheels === "number")
console.log(`the entity has $vehicle.wheels wheels`)

log( wheels : 4, topSpeed: 20, )

without example typeof,

function log(vehicle: ) 
// ✅ Works, item.wheels is now of type unknown
if ('wheels' in vehicle)
console.log(`the entity has $vehicle.wheels wheels`)

log( wheels : 4, topSpeed: 20, )

Another change made in in operator is the key key in obj now forced string | number | symbol, before key An unrestricted type was allowed to have a parameter type.

IEEE The Standard for Floating-Point Arithmetic is a technical standard for floating-point arithmetic. It is implemented equally for all languages ​​that use floating-point.

IEEE 754 imagination suggests that NaN Values ​​are never equal.

NaN === NaN // false
NaN !== NaN // false

How then can we compare them properly? In Javascript, we can use Number.isNaN utility function.

Number.isNaN(NaN) // true
Number.isNaN(0) // false

as part of typescript 4.9 release, the compiler will give an error when comparing NaN value.

function logValue(value: Number | NaN) 
// ❌ Error: This condition will always return 'true'
// Did you mean '!Number.isNaN(value)'
if (value !== NaN)
console.log('It is NaN');

console.log(value);

This convenient error will save many debugging hours, especially for beginners.

Overall this is a great release. I see myself using satisfies operator for quite a few scenarios. I am sure it will become popular soon. Only this feature is worth the upgrade.

There are also, as has now become common, some performance improvements.

I didn’t go into much detail but you can find out more here:

  • Customization #1: change forEachChild a. to use table of functions instead switch Statement Here,
  • Customization #2: add a jump-table for visitEachChild Here,
  • Customization #3: Customize replacement type Here,

Leave a Reply