typescript latest features

TypeScript New & Latest Features


What is index signatures in Typescript?

TypeScript has a feature called index signatures. These signatures are a way to signal to the type system that users can access arbitrarily-named properties. So in simple words by using index signature, we can defines properties in models that are not pre-known or may be dynamic properties.

export interface Employee {
  name: string;
  empId: number | string;
  [propName: string]: string | number;  // Extra properties are caught by this index signature.
}
// Here 'experience' property is not directly exists in model but is a valid property of type string or number.
emp : Employee   = {
    name: 'JS',
    empId: '12345',
    experience: 7
  }

// type 'Date' is not assignable to type 'string | number'.
 emp : Employee   = {
    name: 'JS',
    empId: '12345',
    dob: new Date()   // this is not valid
  }

In the above example, Employee model has an index signature that says any accessed property that’s not already listed should have the type string | number.


How to restructure import paths in Typescript?

Import is like a key of a typescript project and used to include files from different paths or locations.

We have seen imports like ‘../common’, ‘../../common’, ‘../../../common’ or even ‘../../../../common’. Such type of imports are always painful and make code unreadble.

So by using tsconfig compilerOptions : { paths : { … } } options , we can resolve such paths. There are a “compilerOption” option call “paths” which enables us to set up path mappings that we can use in our imports.

 "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@components/*": ["app/components/*"]
    },
  }
Before: import { IHome } from '../home/models/model.interface';
After: import {IHome} from '@components/home/models/model.interface';


Before : import { Employee } from "./../../employee/employee.component";
After: import { Employee } from "@components/employee/employee.component";


TypeScript 4.0: Define Class Property Inference from Constructors

export class EmployeeComponent {
 employeeId; 
  constructor(id: number) {
  }
}

So this will give a compile error like “Member ’employeeId’ implicitly has an ‘any’ type.” if we enable noImplicitAny property.

{
  "compileOnSave": false,
  "compilerOptions": {
    "noImplicitAny": true,
  }
}

Now TypeScript 4.0 can now use control flow analysis to determine the types of properties in classes when noImplicitAny is enabled by using its Constructor. That is called “Class Property Inference from Constructors".
By this, we can assign a value from the constructor (only).

export class EmployeeComponent {
 employeeId; 
  constructor(id: number) {
	thie.employeeId = id; // it will work and now type of employeeId is number.  
  }
}


What is the difference between logical OR (||) and nullish coalescing (??).

The OR operator || uses the right value if left is falsy, while the nullish coalescing operator ?? uses the right value if left is nullish means null or undefined. These operators are often used to provide a default value if the first one is missing.

nullish coalescing has been introduced in Typescript 3.7 Version.

Note: in JavaScript, every nullish value is also falsey (but not every falsey value is nullish).

0 || 'JSMount' > "JSMount"
0 ?? 'JSmount' > 0
"" || 'Hello' > "Hello"
"" ?? 'Hello' > ""
null || 'JS' > "JS"
null ?? 'JS' > "JS"
undefined || 'JS' > "JS"
undefined ?? 'JS' > "JS"


What are Compound assignment operators?

Compound assignment operators apply an operator to two values, and then assign the result to the left side.

// Addition
// a = a + b
a += b;

// Subtraction
// a = a - b
a -= b;

In Typescript there were three notable exceptions: logical and (&&), logical or (||), and nullish coalescing (??).

That’s why TypeScript 4.0 supports a new ECMAScript feature to add three new assignment operators: &&=, ||=, and ??= same as an above compound operator.

a = a || b can written as now 'a ||= b'.

let arr: string[];
(arr ?? (arr = [])).push("hello");

// After
(arr ??= []).push("hello");


Typescript 3.9: CommonJS Auto-Imports in JavaScript

Typescript 3.9 > One great new improvement is in auto-imports in JavaScript files using CommonJS modules.

In older versions, TypeScript always assumed that regardless of your file, you wanted an ECMAScript-style import like

import * as fs from "fs";

But now can use CommonJS-style require(…) imports like so

const fs = require("fs");


TypeScript 3.8: ECMAScript Private Fields with # symbol.

The main take here is that private in TypeScript is not so private, and it feels convenient only at the TypeScript level, not for “real privacy”.

So TypeScript 3.8 brings support for ECMAScript’s private fields. Private fields start with a # character. Sometimes we call these private names.
Every private field name is uniquely scoped to its containing class.

class JSMount {
  #key = 1234;
}

class Java extends JSMount {
  #key = '1234'; // This is valid
}

The above code is completely fine because of uniquely scoped the key variable to its class. But this can not be achieved with the private modifier.

Now let’s see the same code with the private modifier.

class A {
  private key = 453; // this is private with number type
}
class B extends A {
  private key = '123'; // this is private with string type and it will give compile error

  getKey() {
    return this.key;
  }
}

Property ‘key’ in type ‘B’ is not assignable to the same property in base type ‘A’. but the same behavior we can achieve with # private.

When using the private keyword, privacy is only enforced at compile-time/design-time which means that at runtime, it acts entirely like a normal property and there’s no way to tell that it was declared with a private modifier.

On the other hand, ECMAScript’s # privates are completely inaccessible outside of the class. This hard privacy is really useful for strictly ensuring that nobody can make use of any of your internals.
If you’re a library author, removing or renaming a private field should never cause a breaking change.

let a = new A();
console.log(a['key']) // can access and will print output
console.log(a.key) // just compile error showing here but it will also print output.

class B {
  #myKey = 908;
}

let b = new B();
console.log(b['#myKey']) // can not access it will print undefined.
console.log(b.#myKey) // Error: Private field '#myKey' must be declared in an enclosing class


TypeScript 3.7: What is Optional Chaining?

Optional chaining is the new "?." operator for optional property accesses. So when a variable is defined on which this operator applied then only next chain operation will be computed but when applied operator variable is null or undefined, just return undefined.”

// if foo is not defined it will return undefined.
let x = foo?.bar.baz();

// Before
if (foo && foo.bar && foo.bar.baz) {
  // ...
}

// After
if (foo?.bar?.baz) {
  // ...
}


What are Assertion Functions in Version 3.7?

Specific functions that throw an error if something unexpected happened.
They’re called “assertion” functions. As an example, Node.js has a dedicated function for this called assert.
In this example if id isn’t equal to 12, then assert will throw an AssertionError.

assert(id === 12);


What are Uncalled Function Checks?

Sometimes we forgot to invoke a function that has 0 arguments or its name as a property name. Example are : isAuthenticate() or isEmployee().

So Typescript 3.7 – it throws compile error for such cases.

Note: Error is: This condition will always return true since the function is always defined. Did you mean to call it instead?

But you can opt this feature when you enable “strictNullChecks“: true, in tsconfig.json file.

class Js {
  isAuthenticate() : boolean {
    return false;
  }

  checkUser() {
    if(this.isAuthenticate) { // This will give error
      console.log('Hello')
    }
  }
}

More on Typescript:
https://www.typescriptlang.org/docs/handbook/release-notes/overview.html

Typescript New & Latest Features

Typescript New & Latest Features

Leave a Reply

Your email address will not be published.