TypeScript 2.0
Null- and undefined-aware types
TypeScript has two special types, Null and Undefined, that have the valuesnull
andundefined
respectively.Previously it was not possible to explicitly name these types, butnull
andundefined
may now be used as type names regardless of type checking mode.
The type checker previously considerednull
andundefined
assignable to anything.Effectively,null
andundefined
were valid values ofevery type and it wasn’t possible to specifically exclude them (and therefore not possible to detect erroneous use of them).
--strictNullChecks
strictNullChecks
switches to a new strict null checking mode.
In strict null checking mode, thenull
andundefined
values arenot in the domain of every type and are only assignable to themselves andany
(the one exception being thatundefined
is also assignable tovoid
).So, whereasT
andT | undefined
are considered synonymous in regular type checking mode (becauseundefined
is considered a subtype of anyT
), they are different types in strict type checking mode, and onlyT | undefined
permitsundefined
values. The same is true for the relationship ofT
toT | null
.
Example
ts
// Compiled with --strictNullChecksletx:number;lety:number |undefined;letz:number |null |undefined;x =1;// Oky =1;// Okz =1;// Okx =undefined;// Errory =undefined;// Okz =undefined;// Okx =null;// Errory =null;// Errorz =null;// Okx =y;// Errorx =z;// Errory =x;// Oky =z;// Errorz =x;// Okz =y;// Ok
Assigned-before-use checking
In strict null checking mode the compiler requires every reference to a local variable of a type that doesn’t includeundefined
to be preceded by an assignment to that variable in every possible preceding code path.
Example
ts
// Compiled with --strictNullChecksletx:number;lety:number |null;letz:number |undefined;x;// Error, reference not preceded by assignmenty;// Error, reference not preceded by assignmentz;// Okx =1;y =null;x;// Oky;// Ok
The compiler checks that variables are definitely assigned by performingcontrol flow based type analysis. See later for further details on this topic.
Optional parameters and properties
Optional parameters and properties automatically haveundefined
added to their types, even when their type annotations don’t specifically includeundefined
.For example, the following two types are identical:
ts
// Compiled with --strictNullCheckstypeT1 = (x?:number)=>string;// x has type number | undefinedtypeT2 = (x?:number |undefined)=>string;// x has type number | undefined
Non-null and non-undefined type guards
A property access or a function call produces a compile-time error if the object or function is of a type that includesnull
orundefined
.However, type guards are extended to support non-null and non-undefined checks.
Example
ts
// Compiled with --strictNullChecksdeclarefunctionf(x:number):string;letx:number |null |undefined;if (x) {f(x);// Ok, type of x is number here}else {f(x);// Error, type of x is number? here}leta =x !=null ?f(x) :"";// Type of a is stringletb =x &&f(x);// Type of b is string | 0 | null | undefined
Non-null and non-undefined type guards may use the==
,!=
,===
, or!==
operator to compare tonull
orundefined
, as inx != null
orx === undefined
.The effects on subject variable types accurately reflect JavaScript semantics (e.g. double-equals operators check for both values no matter which one is specified whereas triple-equals only checks for the specified value).
Dotted names in type guards
Type guards previously only supported checking local variables and parameters.Type guards now support checking “dotted names” consisting of a variable or parameter name followed by one or more property accesses.
Example
ts
interfaceOptions {location?: {x?:number;y?:number;};}functionfoo(options?:Options) {if (options &&options.location &&options.location.x) {constx =options.location.x;// Type of x is number}}
Type guards for dotted names also work with user defined type guard functions and thetypeof
andinstanceof
operators and do not depend on thestrictNullChecks
compiler option.
A type guard for a dotted name has no effect following an assignment to any part of the dotted name.For example, a type guard forx.y.z
will have no effect following an assignment tox
,x.y
, orx.y.z
.
Expression operators
Expression operators permit operand types to includenull
and/orundefined
but always produce values of non-null and non-undefined types.
ts
// Compiled with --strictNullChecksfunctionsum(a:number |null,b:number |null) {returna +b;// Produces value of type number}
The&&
operator addsnull
and/orundefined
to the type of the right operand depending on which are present in the type of the left operand, and the||
operator removes bothnull
andundefined
from the type of the left operand in the resulting union type.
ts
// Compiled with --strictNullChecksinterfaceEntity {name:string;}letx:Entity |null;lets =x &&x.name;// s is of type string | nulllety =x || {name:"test" };// y is of type Entity
Type widening
Thenull
andundefined
types arenot widened toany
in strict null checking mode.
ts
letz =null;// Type of z is null
In regular type checking mode the inferred type ofz
isany
because of widening, but in strict null checking mode the inferred type ofz
isnull
(and therefore, absent a type annotation,null
is the only possible value forz
).
Non-null assertion operator
A new!
post-fix expression operator may be used to assert that its operand is non-null and non-undefined in contexts where the type checker is unable to conclude that fact.Specifically, the operationx!
produces a value of the type ofx
withnull
andundefined
excluded.Similar to type assertions of the forms<T>x
andx as T
, the!
non-null assertion operator is simply removed in the emitted JavaScript code.
ts
// Compiled with --strictNullChecksfunctionvalidateEntity(e?:Entity) {// Throw exception if e is null or invalid entity}functionprocessEntity(e?:Entity) {validateEntity(e);lets =e!.name;// Assert that e is non-null and access name}
Compatibility
The new features are designed such that they can be used in both strict null checking mode and regular type checking mode.In particular, thenull
andundefined
types are automatically erased from union types in regular type checking mode (because they are subtypes of all other types), and the!
non-null assertion expression operator is permitted but has no effect in regular type checking mode. Thus, declaration files that are updated to use null- and undefined-aware types can still be used in regular type checking mode for backwards compatibility.
In practical terms, strict null checking mode requires that all files in a compilation are null- and undefined-aware.
Control flow based type analysis
TypeScript 2.0 implements a control flow-based type analysis for local variables and parameters.Previously, the type analysis performed for type guards was limited toif
statements and?:
conditional expressions and didn’t include effects of assignments and control flow constructs such asreturn
andbreak
statements.With TypeScript 2.0, the type checker analyses all possible flows of control in statements and expressions to produce the most specific type possible (thenarrowed type) at any given location for a local variable or parameter that is declared to have a union type.
Example
ts
functionfoo(x:string |number |boolean) {if (typeofx ==="string") {x;// type of x is string herex =1;x;// type of x is number here}x;// type of x is number | boolean here}functionbar(x:string |number) {if (typeofx ==="number") {return;}x;// type of x is string here}
Control flow based type analysis is particularly relevant instrictNullChecks
mode because nullable types are represented using union types:
ts
functiontest(x:string |null) {if (x ===null) {return;}x;// type of x is string in remainder of function}
Furthermore, instrictNullChecks
mode, control flow based type analysis includesdefinite assignment analysis for local variables of types that don’t permit the valueundefined
.
ts
functionmumble(check:boolean) {letx:number;// Type doesn't permit undefinedx;// Error, x is undefinedif (check) {x =1;x;// Ok}x;// Error, x is possibly undefinedx =2;x;// Ok}
Tagged union types
TypeScript 2.0 implements support for tagged (or discriminated) union types.Specifically, the TS compiler now support type guards that narrow union types based on tests of a discriminant property and furthermore extend that capability toswitch
statements.
Example
ts
interfaceSquare {kind:"square";size:number;}interfaceRectangle {kind:"rectangle";width:number;height:number;}interfaceCircle {kind:"circle";radius:number;}typeShape =Square |Rectangle |Circle;functionarea(s:Shape) {// In the following switch statement, the type of s is narrowed in each case clause// according to the value of the discriminant property, thus allowing the other properties// of that variant to be accessed without a type assertion.switch (s.kind) {case"square":returns.size *s.size;case"rectangle":returns.width *s.height;case"circle":returnMath.PI *s.radius *s.radius;}}functiontest1(s:Shape) {if (s.kind ==="square") {s;// Square}else {s;// Rectangle | Circle}}functiontest2(s:Shape) {if (s.kind ==="square" ||s.kind ==="rectangle") {return;}s;// Circle}
Adiscriminant property type guard is an expression of the formx.p == v
,x.p === v
,x.p != v
, orx.p !== v
, wherep
andv
are a property and an expression of a string literal type or a union of string literal types.The discriminant property type guard narrows the type ofx
to those constituent types ofx
that have a discriminant propertyp
with one of the possible values ofv
.
Note that we currently only support discriminant properties of string literal types.We intend to later add support for boolean and numeric literal types.
Thenever
type
TypeScript 2.0 introduces a new primitive typenever
.Thenever
type represents the type of values that never occur.Specifically,never
is the return type for functions that never return andnever
is the type of variables under type guards that are never true.
Thenever
type has the following characteristics:
never
is a subtype of and assignable to every type.- No type is a subtype of or assignable to
never
(exceptnever
itself). - In a function expression or arrow function with no return type annotation, if the function has no
return
statements, or onlyreturn
statements with expressions of typenever
, and if the end point of the function is not reachable (as determined by control flow analysis), the inferred return type for the function isnever
. - In a function with an explicit
never
return type annotation, allreturn
statements (if any) must have expressions of typenever
and the end point of the function must not be reachable.
Becausenever
is a subtype of every type, it is always omitted from union types and it is ignored in function return type inference as long as there are other types being returned.
Some examples of functions returningnever
:
ts
// Function returning never must have unreachable end pointfunctionerror(message:string):never {thrownewError(message);}// Inferred return type is neverfunctionfail() {returnerror("Something failed");}// Function returning never must have unreachable end pointfunctioninfiniteLoop():never {while (true) {}}
Some examples of use of functions returningnever
:
ts
// Inferred return type is numberfunctionmove1(direction:"up" |"down") {switch (direction) {case"up":return1;case"down":return -1;}returnerror("Should never get here");}// Inferred return type is numberfunctionmove2(direction:"up" |"down") {returndirection ==="up"?1:direction ==="down"? -1:error("Should never get here");}// Inferred return type is Tfunctioncheck<T>(x:T |undefined) {returnx ||error("Undefined value");}
Becausenever
is assignable to every type, a function returningnever
can be used when a callback returning a more specific type is required:
ts
functiontest(cb: ()=>string) {lets =cb();returns;}test(()=>"hello");test(()=>fail());test(()=> {thrownewError();});
Read-only properties and index signatures
A property or index signature can now be declared with thereadonly
modifier.
Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.
In addition, entities areimplicitly read-only in several situations:
- A property declared with a
get
accessor and noset
accessor is considered read-only. - In the type of an enum object, enum members are considered read-only properties.
- In the type of a module object, exported
const
variables are considered read-only properties. - An entity declared in an
import
statement is considered read-only. - An entity accessed through an ES2015 namespace import is considered read-only (e.g.
foo.x
is read-only whenfoo
is declared asimport * as foo from "foo"
).
Example
ts
interfacePoint {readonlyx:number;readonlyy:number;}varp1:Point = {x:10,y:20 };p1.x =5;// Error, p1.x is read-onlyvarp2 = {x:1,y:1 };varp3:Point =p2;// Ok, read-only alias for p2p3.x =5;// Error, p3.x is read-onlyp2.x =5;// Ok, but also changes p3.x because of aliasing
ts
classFoo {readonlya =1;readonlyb:string;constructor() {this.b ="hello";// Assignment permitted in constructor}}
ts
leta:Array<number> = [0,1,2,3,4];letb:ReadonlyArray<number> =a;b[5] =5;// Error, elements are read-onlyb.push(5);// Error, no push method (because it mutates array)b.length =3;// Error, length is read-onlya =b;// Error, mutating methods are missing
Specifying the type ofthis
for functions
Following up on specifying the type ofthis
in a class or an interface, functions and methods can now declare the type ofthis
they expect.
By default the type ofthis
inside a function isany
.Starting with TypeScript 2.0, you can provide an explicitthis
parameter.this
parameters are fake parameters that come first in the parameter list of a function:
ts
functionf(this:void) {// make sure `this` is unusable in this standalone function}
this
parameters in callbacks
Libraries can also usethis
parameters to declare how callbacks will be invoked.
Example
ts
interfaceUIElement {addClickListener(onclick: (this:void,e:Event)=>void):void;}
this: void
means thataddClickListener
expectsonclick
to be a function that does not require athis
type.
Now if you annotate calling code withthis
:
ts
classHandler {info:string;onClickBad(this:Handler,e:Event) {// oops, used this here. using this callback would crash at runtimethis.info =e.message;}}leth =newHandler();uiElement.addClickListener(h.onClickBad);// error!
--noImplicitThis
A new flag is also added in TypeScript 2.0 to flag all uses ofthis
in functions without an explicit type annotation.
Glob support intsconfig.json
Glob support is here!! Glob support has beenone of the most requested features.
Glob-like file patterns are supported by two propertiesinclude
andexclude
.
Example
{" ": {" ":"commonjs"," ":true," ":true," ":true," ":"../../built/local/tsc.js"," ":true}," ": ["src/**/*"]," ": ["node_modules","**/*.spec.ts"]}
The supported glob wildcards are:
*
matches zero or more characters (excluding directory separators)?
matches any one character (excluding directory separators)**/
recursively matches any subdirectory
If a segment of a glob pattern includes only*
or.*
, then only files with supported extensions are included (e.g..ts
,.tsx
, and.d.ts
by default with.js
and.jsx
ifallowJs
is set to true).
If thefiles
andinclude
are both left unspecified, the compiler defaults to including all TypeScript (.ts
,.d.ts
and.tsx
) files in the containing directory and subdirectories except those excluded using theexclude
property. JS files (.js
and.jsx
) are also included ifallowJs
is set to true.
If thefiles
orinclude
properties are specified, the compiler will instead include the union of the files included by those two properties.Files in the directory specified using theoutDir
compiler option are always excluded unless explicitly included via thefiles
property (even when theexclude
property is specified).
Files included usinginclude
can be filtered using theexclude
property.However, files included explicitly using thefiles
property are always included regardless ofexclude
.Theexclude
property defaults to excluding thenode_modules
,bower_components
, andjspm_packages
directories when not specified.
Module resolution enhancements: BaseUrl, Path mapping, rootDirs and tracing
TypeScript 2.0 provides a set of additional module resolution knops toinform the compiler where to find declarations for a given module.
SeeModule Resolution documentation for more details.
Base URL
Using abaseUrl
is a common practice in applications using AMD module loaders where modules are “deployed” to a single folder at run-time.All module imports with bare specifier names are assumed to be relative to thebaseUrl
.
Example
{" ": {" ":"./modules"}}
Now imports to"moduleA"
would be looked up in./modules/moduleA
ts
importAfrom"moduleA";
Path mapping
Sometimes modules are not directly located underbaseUrl.Loaders use a mapping configuration to map module names to files at run-time, seeRequireJs documentation andSystemJS documentation.
The TypeScript compiler supports the declaration of such mappings usingpaths
property intsconfig.json
files.
Example
For instance, an import to a module"jquery"
would be translated at runtime to"node_modules/jquery/dist/jquery.slim.min.js"
.
{" ": {" ":"./node_modules"," ": {"jquery": ["jquery/dist/jquery.slim.min"]}}
Usingpaths
also allows for more sophisticated mappings including multiple fall back locations.Consider a project configuration where only some modules are available in one location, and the rest are in another.
Virtual Directories withrootDirs
Using ‘rootDirs’, you can inform the compiler of theroots making up this “virtual” directory;and thus the compiler can resolve relative modules imports within these “virtual” directoriesas if they were merged together in one directory.
Example
Given this project structure:
src └── views └── view1.ts (imports './template1') └── view2.ts generated └── templates └── views └── template1.ts (imports './view2')
A build step will copy the files in/src/views
and/generated/templates/views
to the same directory in the output.At run-time, a view can expect its template to exist next to it, and thus should import it using a relative name as"./template"
.
rootDirs
specify a list ofroots whose contents are expected to merge at run-time.So following our example, thetsconfig.json
file should look like:
{" ": {" ": ["src/views","generated/templates/views"]}}
Tracing module resolution
traceResolution
offers a handy way to understand how modules have been resolved by the compiler.
shell
tsc --traceResolution
Shorthand ambient module declarations
If you don’t want to take the time to write out declarations before using a new module, you can now just use a shorthand declaration to get started quickly.
declarations.d.ts
ts
declaremodule"hot-new-module";
All imports from a shorthand module will have the any type.
ts
importx, {y }from"hot-new-module";x(y);
Wildcard character in module names
Importing non-code resources using module loaders extension (e.g.AMD orSystemJS) has not been easy before;previously an ambient module declaration had to be defined for each resource.
TypeScript 2.0 supports the use of the wildcard character (*
) to declare a “family” of module names;this way, a declaration is only required once for an extension, and not for every resource.
Example
ts
declaremodule"*!text" {constcontent:string;exportdefaultcontent;}// Some do it the other way around.declaremodule"json!*" {constvalue:any;exportdefaultvalue;}
Now you can import things that match"*!text"
or"json!*"
.
ts
importfileContentfrom"./xyz.txt!text";importdatafrom"json!http://example.com/data.json";console.log(data,fileContent);
Wildcard module names can be even more useful when migrating from an un-typed code base.Combined with Shorthand ambient module declarations, a set of modules can be easily declared asany
.
Example
ts
declaremodule"myLibrary/*";
All imports to any module undermyLibrary
would be considered to have the typeany
by the compiler;thus, shutting down any checking on the shapes or types of these modules.
ts
import {readFile }from"myLibrary/fileSystem/readFile`;readFile();// readFile is 'any'
Support for UMD module definitions
Some libraries are designed to be used in many module loaders, or with no module loading (global variables).These are known asUMD orIsomorphic modules.These libraries can be accessed through either an import or a global variable.
For example:
math-lib.d.ts
ts
exportconstisPrime(x:number):boolean;exportasnamespacemathLib;
The library can then be used as an import within modules:
ts
import {isPrime }from"math-lib";isPrime(2);mathLib.isPrime(2);// ERROR: can't use the global definition from inside a module
It can also be used as a global variable, but only inside of a script.(A script is a file with no imports or exports.)
ts
mathLib.isPrime(2);
Optional class properties
Optional properties and methods can now be declared in classes, similar to what is already permitted in interfaces.
Example
ts
classBar {a:number;b?:number;f() {return1;}g?():number;// Body of optional method can be omittedh?() {return2;}}
When compiled instrictNullChecks
mode, optional properties and methods automatically haveundefined
included in their type. Thus, theb
property above is of typenumber | undefined
and theg
method above is of type(() => number) | undefined
.Type guards can be used to strip away theundefined
part of the type:
ts
functiontest(x:Bar) {x.a;// numberx.b;// number | undefinedx.f;// () => numberx.g;// (() => number) | undefinedletf1 =x.f();// numberletg1 =x.g &&x.g();// number | undefinedletg2 =x.g ?x.g() :0;// number}
Private and Protected Constructors
A class constructor may be markedprivate
orprotected
.A class with a private constructor cannot be instantiated outside the class body, and cannot be extended.A class with a protected constructor cannot be instantiated outside the class body, but can be extended.
Example
ts
classSingleton {privatestaticinstance:Singleton;privateconstructor() {}staticgetInstance() {if (!Singleton.instance) {Singleton.instance =newSingleton();}returnSingleton.instance;}}lete =newSingleton();// Error: constructor of 'Singleton' is private.letv =Singleton.getInstance();
Abstract properties and accessors
An abstract class can declare abstract properties and/or accessors.Any sub class will need to declare the abstract properties or be marked as abstract.Abstract properties cannot have an initializer.Abstract accessors cannot have bodies.
Example
ts
abstractclassBase {abstractname:string;abstractgetvalue();abstractsetvalue(v:number);}classDerivedextendsBase {name ="derived";value =1;}
Implicit index signatures
An object literal type is now assignable to a type with an index signature if all known properties in the object literal are assignable to that index signature. This makes it possible to pass a variable that was initialized with an object literal as parameter to a function that expects a map or dictionary:
ts
functionhttpService(path:string,headers: { [x:string]:string }) {}constheaders = {"Content-Type":"application/x-www-form-urlencoded",};httpService("", {"Content-Type":"application/x-www-form-urlencoded" });// OkhttpService("",headers);// Now ok, previously wasn't
Including built-in type declarations with--lib
Getting to ES6/ES2015 built-in API declarations were only limited totarget: ES6
.Enterlib
; withlib
you can specify a list of built-in API declaration groups that you can choose to include in your project.For instance, if you expect your runtime to have support forMap
,Set
andPromise
(e.g. most evergreen browsers today), just include--lib es2015.collection,es2015.promise
.Similarly you can exclude declarations you do not want to include in your project, e.g. DOM if you are working on a node project using--lib es5,es6
.
Here is a list of available API groups:
- dom
- webworker
- es5
- es6 / es2015
- es2015.core
- es2015.collection
- es2015.iterable
- es2015.promise
- es2015.proxy
- es2015.reflect
- es2015.generator
- es2015.symbol
- es2015.symbol.wellknown
- es2016
- es2016.array.include
- es2017
- es2017.object
- es2017.sharedmemory
- scripthost
Example
bash
tsc --target es5 --lib es5,es2015.promise
"compilerOptions": {" ": ["es5","es2015.promise"]}
Flag unused declarations with--noUnusedParameters
and--noUnusedLocals
TypeScript 2.0 has two new flags to help you maintain a clean code base.noUnusedParameters
flags any unused function or method parameters errors.noUnusedLocals
flags any unused local (un-exported) declaration like variables, functions, classes, imports, etc…Also, unused private members of a class would be flagged as errors undernoUnusedLocals
.
Example
ts
importB, {readFile }from"./b";// ^ Error: `B` declared but never usedreadFile();exportfunctionwrite(message:string,args:string[]) {// ^^^^ Error: 'arg' declared but never used.console.log(message);}
Parameters declaration with names starting with_
are exempt from the unused parameter checking.e.g.:
ts
functionreturnNull(_a) {// OKreturnnull;}
Module identifiers allow for.js
extension
Before TypeScript 2.0, a module identifier was always assumed to be extension-less;for instance, given an import asimport d from "./moduleA.js"
, the compiler looked up the definition of"moduleA.js"
in./moduleA.js.ts
or./moduleA.js.d.ts
.This made it hard to use bundling/loading tools likeSystemJS that expect URI’s in their module identifier.
With TypeScript 2.0, the compiler will look up definition of"moduleA.js"
in./moduleA.ts
or./moduleA.d.t
.
Support ‘target : es5’ with ‘module: es6’
Previously flagged as an invalid flag combination,target: es5
and ‘module: es6’ is now supported.This should facilitate using ES2015-based tree shakers likerollup.
Trailing commas in function parameter and argument lists
Trailing comma in function parameter and argument lists are now allowed.This is an implementation for aStage-3 ECMAScript proposal that emits down to valid ES3/ES5/ES6.
Example
ts
functionfoo(bar:Bar,baz:Baz// trailing commas are OK in parameter lists) {// Implementation...}foo(bar,baz// and in argument lists);
New--skipLibCheck
TypeScript 2.0 adds a newskipLibCheck
compiler option that causes type checking of declaration files (files with extension.d.ts
) to be skipped.When a program includes large declaration files, the compiler spends a lot of time type checking declarations that are already known to not contain errors, and compile times may be significantly shortened by skipping declaration file type checks.
Since declarations in one file can affect type checking in other files, some errors may not be detected whenskipLibCheck
is specified.For example, if a non-declaration file augments a type declared in a declaration file, errors may result that are only reported when the declaration file is checked.However, in practice such situations are rare.
Allow duplicate identifiers across declarations
This has been one common source of duplicate definition errors.Multiple declaration files defining the same members on interfaces.
TypeScript 2.0 relaxes this constraint and allows duplicate identifiers across blocks, as long as they haveidentical types.
Within the same block duplicate definitions are still disallowed.
Example
ts
interfaceError {stack?:string;}interfaceError {code?:string;path?:string;stack?:string;// OK}
New--declarationDir
declarationDir
allows for generating declaration files in a different location than JavaScript files.
The TypeScript docs are an open source project. Help us improve these pagesby sending a Pull Request ❤
Last updated: Jul 14, 2025