Before you go, check out these stories!

Hackernoon logoTypescript 3.9: What got changed? by@heypran

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:


Join Hacker Noon

Create your free account to unlock your custom reading experience.