Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News Typescript 4.2 Released, Improves Types and Developer Experience

Typescript 4.2 Released, Improves Types and Developer Experience

This item in japanese

The TypeScript team announced the release of TypeScript 4.2, which features more flexible type annotations, stricter checks, extra configuration options, and a few breaking changes. Tuple types now allow rest arguments in any position (instead of only in last position). Type aliases are no longer expanded in type error messages, providing a better developer experience.

TypeScript 4.2 supports rest arguments in any position in tuple types:

type T1 = [...string[], number];  // Zero or more strings followed by a number
type T2 = [number, ...boolean[], string, string];  // Number followed by zero or more booleans followed by two strings

In previous versions, the rest arguments had to be in last position (e.g., type T1 = [number, ...string[]];. It was thus not possible to strongly type functions with a variable number of parameters that ended with a fixed set of parameters:

function f1(...args: [...string[], number]) {
    const strs = args.slice(0, -1) as string[];
    const num = args[args.length - 1] as number;
    // ...

f1('abc', 5);
f1('abc', 'def', 5);
f1('abc', 'def', 5, 6);  // Error

The function f1 has an indefinite number of arguments of type string, followed by a parameter of type number. f1 can now be typed accurately. Multiple rest elements are not permitted. An optional element cannot precede a required element or follow a rest element. Types are normalized as follows:

type Tup3<T extends unknown[], U extends unknown[], V extends unknown[]> = [...T, ...U, ...V];

type TN1 = Tup3<[number], string[], [number]>;  // [number, ...string[], number]
// Optional element following required element
type TN2 = Tup3<[number], [string?], [boolean]>;  // [number, string | undefined, boolean]
type TN3 = Tup3<[number], string[], [boolean?]>;  // [number, ...(string | boolean | undefined)[]]
type TN4 = Tup3<[number], string[], boolean[]>;  // [number, ...(string | boolean)[]]
type TN5 = Tup3<string[], number[], boolean[]>;  // (string | number | boolean)[] 


TypeScript 4.2 provides a better developer experience when using type aliases:
Smarter type alias preservation

The previous example shows that the type alias BasicPrimitive that was previously expanded (normalized) in some contexts (i.e. to number | string | boolean) is no longer so. The release note emphasized the improved developer experience in several parts of the TypeScript experience:

You can avoid some unfortunately humongous types getting displayed, and that often translates to getting better .d.ts file output, error messages, and in-editor type displays in quick info and signature help. This can help TypeScript feel a little bit more approachable for newcomers.

The abstract modifier can now be used on constructor signatures.

abstract  class  Shape  {
  abstract  getArea():  number;
// Error! Can't instantiate an abstract class.  
new  Shape();

interface  HasArea  {  
getArea():  number;  
// Error! Cannot assign an abstract constructor type to a non-abstract constructor type.  
let  Ctor:  new  ()  =>  HasArea  =  Shape;

// Works!
let  Ctor:  abstract  new  ()  =>  HasArea  =  Shape;  
//          ^^^^^^^^

The new semantics for the abstract modifier allows writing mixin factories in a way that supports abstract classes.

Destructured variables can now be explicitly marked as unused. Some developers would previously write:

const [Input, /* state */ , /* actions */, meta] = input 

for better maintainability and readability instead of

const [Input, , , meta] = input 

Those developers can now prefix unused variables with an underscore:

const [Input, _state , _actions, meta] = input

The new version of TypeScript also adds stricter checks for the in operator: "foo" in 42 will trigger a type error. TypeScript’s uncalled function checks now apply within && and || expressions. Setting the noPropertyAccessFromIndexSignature flag no longer makes it possible to use property access with the dot operator (e.g., when a type had a string index signature. The explainFiles compiler flag (e.g., tsc --explainFiles) has the compiler produce detailed information about resolved and processed files:

  Library referenced via 'es5' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2015.d.ts'
  Library referenced via 'es2015' from file 'TS_Compiler_Directory/4.2.2/lib/lib.es2016.d.ts'

... More Library References...

  Matched by include pattern '**/*' in 'tsconfig.json'

TypeScript 4.2 also contains a few breaking changes. Type arguments in JavaScript are not parsed as type arguments, meaning that the valid TypeScript code f<T>(100) will be parsed in a JavaScript file as per the JavaScript spec, i.e. as (f < T) > (100). .d.ts extensions cannot be used In import paths: import { Foo } from "./foo.d.ts"; may be replaced with any of the following:

import  {  Foo  }  from  "./foo.js";
import  {  Foo  }  from  "./foo";
import  {  Foo  }  from  "./foo/index.js";

TypeScript 4.2 gets TypeScript ever closer to its goal of accurately typing JavaScript at any scale, anywhere JavaScript runs. With every release, however, TypeScript increases its complexity. One developer noted:

With so many updates to TypeScript, people start wondering if there will be a point [… when] it becomes too complicated to know it all, just like C++.

TypeScript contains additional features and breaking changes. Developers are invited to read the full release note. TypeScript is open-source software available under the Apache 2 license. Contributions and feedback are encouraged via the TypeScript GitHub project and should follow the TypeScript contribution guidelines and Microsoft open-source code of conduct.

Rate this Article