Typescript 3.9: What got changed?

Author profile picture

@heypranPran B.

Curiosity killed the Schrodinger's cat? or did it?

Hey! guys, in this post I will be discussing the changes brought in by typescript 3.9. I will give some code examples and brief descriptions.
Breaking Changes
1. Parsing differences in optional chaining & non-Null assertions
Previous versions: In some cases, using optional chaining (?) with non-null assertions (!) alters the behavior of short-circuiting (optional chaining no longer works) 
Now (3.9): The above no longer happens and code is intuitive. 
import { ec } from 'easy-console';

interface Orders {
  orderDetail?: OrderDetail; // orderDetail: OrderDetail | undefined;

interface OrderDetail {
  item?: Item; // item: Item | undefined;

interface Item {
  price: number;

const order: Orders = { orderDetail: undefined };

const itemPrice: number = order.orderDetail?.item!.price; //

// Before
ec.l(itemPrice); // trying to access property on undefined

// v3.9
ec.l(itemPrice); //undefined

2. Stricter checks on intersections and optional properties
Previous versions: Types derived by using intersection can be assigned to other similar types without stricter checks on the underneath type properties.
Now: There are stricter checks on the types when using the intersection types. So it will not work if the types are exactly not same.
import { ec } from 'easy-console';

interface A {
  a: number; // notice this is 'number'

interface B {
  b: string;

interface C {
  a?: boolean; // notice this is 'boolean'
  b: string;

const x: A & B = { a: 1, b: `s` };

// Before
const y: C = x; // Type 'number' is not assignable to type 'boolean'.

ec.l(`x>>`, x); // { a: 1, b: `s` }
ec.l(`y>>`, y); // { a: 1, b: `s` }

// 3.9
const y: C = x; // error-  Type 'number' is not assignable to type 'boolean'.
3. Stricter check on Intersections derived from different type properties
Before: Intersection of types which have same properties with no overlapping type, collapses to never for that particular particular property.
Now: Intersection of types which have same properties with nothing in common, collapses the whole intersection type to never.
import { ec } from 'easy-console';

interface Category {
  iam: 'categoryType';
  categoryName: string;
  level: number;

interface Product {
  iam: 'productType';
  productName: string;
  productPrice: number;

type Group = Category & Product; // 3.9 whole types becomes never

const group: Group = {
  categoryName: 'Laptops',
  level: 1,
  productName: 'Macbook',
  productPrice: 1234,
  iAm: "never say never",  // in previous version only the particular type becomes

// Before
ec.l('group.iAm =>', group); // previous version - error only on 'iAm' property

// 3.9
ec.l('group.iAm =>', group); // version 3.9 - error on all properties
are Now Invalid JSX Text Characters
Now you cannot use them directly in .tsx files. You will get below errors.
Unexpected token. Did you mean `{'>'}` or `>`?
Unexpected token. Did you mean `{'}'}` or `}`?
5. Type Parameters That Extend any No Longer Act as any
import { ec } from 'easy-console';

function foo<T extends any>(arg: T) {
  ec.l('arg.anyArguments', arg.IwillNotGiveError); // previous versions no error 

function foo<T extends any>(arg: T) {
  ec.l('arg.anyArguments', arg.IwillGiveError); // 3.9 error 
1. Improvements in Inference and Promise.all
In certain cases when while using Promise.all(), the response types of the promises get mismatched in the result. This results in compile time error. This was observed mostly when an undefined type was present. Find below the codebox ( on older version).
2. // @ts-expect-error Comments
It allows you to accept an error where there is a type error. For eg. In a scenario where you are writing a test, and you deliberately want to pass different types of values.
How is it different from @ts-ignore ?
@ts-expect-error will notify you when it is not required. 
describe('Todo', () => {

  it('sample test', () => {
    function expectErr(a: string) {

    // @ts-expect-error
    expectErr(1);    // no error

    // @ts-expect-error
    expectErr("a");  // error

3. Uncalled Function Checks in Conditional Expressions (?:)

In previous versions, typescript was doing a check, whether we have called our functions while using conditions (such as if else) or not. But not on the using the conditional operators (? :). But now it supports the same.
function hasImportantPermissions(): boolean {
    // ...

// Oops!
if (hasImportantPermissions) {
//  ~~~~~~~~~~~~~~~~~~~~~~~
// This condition will always return true since the function is always defined.
// Did you mean to call it instead?
4. Typescript now support "solution style" tsconfig.json files
You can define the several tsconfig in one file, rather than placing in individual directory structure.
// tsconfig.json
    "files": [],
    "references": [
        { "path": "./tsconfig.shared.json" },
        { "path": "./tsconfig.frontend.json" },
        { "path": "./tsconfig.backend.json" },
5. Other minor improvements
  1. CommonJS auto-imports imports like JS (using require statement )
  2. Compile time improvements
  3. Editor improvements- improved support for sublime, vscode, nightly
  4. Editor Code Actions - properly preserve spacing/line breaks
Happy Hacking!
For more detailed information and issue specific pull request, refer the following link: https://devblogs.microsoft.com/typescript/announcing-typescript-3-9-beta/


The Noonification banner

Subscribe to get your daily round-up of top tech stories!