Typescript Handbook v4.1
Typescript Handbook v4.1
Introduction 1.1
Get Started 1.2
TS for Functional Programmers 1.2.1
TS for JS Programers 1.2.2
TS for OOPers 1.2.3
TS for the New Programmers 1.2.4
Tutorials 1.3
Angular 1.3.1
ASP.Net Core 1.3.2
Babel With TypeScript 1.3.3
DOM Manipulation 1.3.4
Gulp 1.3.5
Migrating from Javascript 1.3.6
React 1.3.7
Typescript Tooling in 5 minutes 1.3.8
Handbook 1.4
Basic Types 1.4.1
Interfaces 1.4.2
Functions 1.4.3
Literal Types 1.4.4
Unions and Intersection Types 1.4.5
Classes 1.4.6
Enum 1.4.7
Generics 1.4.8
Handbok Reference 1.5
Advanced Types 1.5.1
Utility Types 1.5.2
Decorators 1.5.3
Declaration Merging 1.5.4
Iterators and Generator 1.5.5
JSX 1.5.6
Mixins 1.5.7
Modules 1.5.8
Module Resolution 1.5.9
Namespaces 1.5.10
Namespaces and Modules 1.5.11
Symbols 1.5.12
Triple-slash Directives 1.5.13
Type Compatibility 1.5.14
Type Inference 1.5.15
Variable Declaration 1.5.16
What's New 1.6
Overview 1.6.1
TypeScript-4.1 1.6.2
TypeScript-4.0 1.6.3
TypeScript-3.9 1.6.4
TypeScript-3.8 1.6.5
TypeScript-3.7 1.6.6
TypeScript-3.6 1.6.7
TypeScript-3.5 1.6.8
TypeScript-3.4 1.6.9
TypeScript-3.3 1.6.10
TypeScript-3.2 1.6.11
TypeScript-3.1 1.6.12
TypeScript-3.0 1.6.13
TypeScript-2.9 1.6.14
TypeScript-2.8 1.6.15
TypeScript-2.7 1.6.16
TypeScript-2.6 1.6.17
TypeScript-2.5 1.6.18
TypeScript-2.4 1.6.19
TypeScript-2.3 1.6.20
TypeScript-2.2 1.6.21
TypeScript-2.1 1.6.22
TypeScript-2.0 1.6.23
TypeScript-1.8 1.6.24
TypeScript-1.7 1.6.25
TypeScript-1.6 1.6.26
TypeScript-1.5 1.6.27
TypeScript-1.4 1.6.28
TypeScript-1.3 1.6.29
TypeScript-1.1 1.6.30
Declaration Files 2.1
Introcutoin 2.1.1
Library Structures 2.1.2
By Example 2.1.3
Do's and Don'ts 2.1.4
Deep Dive 2.1.5
Templates 2.1.6
Modules .d.ts 2.1.6.1
Module: Plugin 2.1.6.2
Module: Class 2.1.6.3
Module: Function 2.1.6.4
Global .d.ts 2.1.6.5
Global Modifying Module 2.1.6.6
Publishing 2.1.7
Consumption 2.1.8
Javascript 2.2
JS Projects Utilizing TypeScript 2.2.1
Type Checking Javascript Files 2.2.2
JSDoc Reference 2.2.3
Createing .d.ts Files from .js files 2.2.4
Project Configuration 2.3
tsconfig.json 2.3.1
Compiler Options 2.3.2
Project Reference 2.3.3
Compiler Options in MSBuild 2.3.4
Integrating with Build Tools 2.3.5
Configuring Watch 2.3.6
Nightly Builds 2.3.7
Handbook v2: Beta 2.4
The Basic 2.4.1
Everyday Types 2.4.2
Narrowing 2.4.3
More on Functions 2.4.4
Object Types 2.4.5
Classes 2.4.6
Modules 2.4.7
Type Manipulation 2.4.8
Creating Types from Types 2.4.8.1
Keyof Type Operator 2.4.8.2
Typeof Type Operator 2.4.8.3
Indexed Access Types 2.4.8.4
Conditional Types 2.4.8.5
TypeScript-Handbook
(4.1)
The PDF is generated by gitbook. The document is based on
Typescript handlebook
You may have written JavaScript in Visual Studio Code, and had editor auto-
completion. Visual Studio Code uses TypeScript under the hood to make it easier to
work with JavaScript.
## Defining Types
You can use a wide variety of design patterns in JavaScript. However, some design
patterns make it difficult for types to be inferred automatically (for example,
patterns that use dynamic programming). To cover these cases, TypeScript supports
an extension of the JavaScript language, which offers places for you to tell
TypeScript what the types should be.
For example, to create an object with an inferred type which includes `name:
string` and `id: number`, you can write:
```ts twoslash
const user = {
name: "Hayes",
id: 0,
};
declaration:
```ts twoslash
interface User {
name: string;
id: number;
}
// ---cut---
const user: User = {
name: "Hayes",
id: 0,
};
If you provide an object that doesn't match the interface you have
provided, TypeScript will warn you:
```ts twoslash
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
You'll see that there are two syntaxes for building types: [Interfaces and Types]
(/play/?e=83#example/types-vs-interfaces). You should prefer `interface`. Use
`type` when you need specific features.
## Composing Types
With TypeScript, you can create complex types by combining simple ones. There are
two popular ways to do so: with Unions, and with Generics.
### Unions
With a union, you can declare that a type could be one of many types. For example,
you can describe a `boolean` type as being either `true` or `false`:
```ts twoslash
type MyBool = true | false;
Note: If you hover over MyBool above, you'll see that it is classed as
boolean . That's a property of the Structural Type System. More on
this below.
Unions provide a way to handle different types too. For example, you may have a
function that takes an `array` or a `string`:
```ts twoslash
function getLength(obj: string | string[]) {
return obj.length;
}
Type Predicate
array Array.isArray(a)
```ts
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;
One of TypeScript's core principles is that type checking focuses on the _shape_
that values have. This is sometimes called "duck typing" or "structural typing".
In a structural type system, if two objects have the same shape, they are
considered to be of the same type.
```ts twoslash
interface Point {
x: number;
y: number;
}
```ts twoslash
// @errors: 2345
interface Point {
x: number;
y: number;
}
If the object or class has all the required properties, TypeScript will
say they match, regardless of the implementation details.
Next Steps
This was a brief overview of the syntax and tools used in everyday
TypeScript. From here, you can:
Static Classes
Additionally, certain constructs from C# and Java such as singletons
and static classes are unnecessary in TypeScript.
OOP in TypeScript
That said, you can still use classes if you like! Some problems are
well-suited to being solved by a traditional OOP hierarchy, and
TypeScript's support for JavaScript classes will make these models even
more powerful. TypeScript supports many common patterns such as
implementing interfaces, inheritance, and static methods.
Types as Sets
In C# or Java, it's meaningful to think of a one-to-one
correspondence between runtime types and their compile-time
declarations.
printPoint(obj); printName(obj);
TypeScript's type system is _structural_, not nominal: We can use `obj` as a
`Pointlike` because it has `x` and `y` properties that are both numbers.
The relationships between types are determined by the properties they contain, not
whether they were declared with some particular relationship.
TypeScript's type system is also _not reified_: There's nothing at runtime that
will tell us that `obj` is `Pointlike`.
In fact, the `Pointlike` type is not present _in any form_ at runtime.
Going back to the idea of _types as sets_, we can think of `obj` as being a member
of both the `Pointlike` set of values and the `Named` set of values.
OOP programmers are often surprised by two particular aspects of structural typing.
```ts twoslash
class Empty {}
has all of the properties that Empty does, because Empty has no
properties. Therefore, this is a valid call!
This may seem surprising, but it's ultimately a very similar relationship
to one enforced in nominal OOP languages. A subclass cannot remove a
property of its base class, because doing so would destroy the natural
subtype relationship between the derived class and its base. Structural
type systems simply identify this relationship implicitly by describing
subtypes in terms of having properties of compatible types.
d t l
Identical Types
Another frequent source of surprise comes with identical types:
class Car {
drive() {
// hit the gas
}
}
class Golfer {
drive() {
// hit the ball far
}
}
// No error?
let w: Car = new Golfer();
Again, this isn't an error because the structures of these classes are
the same. While this may seem like a potential source of confusion, in
practice, identical classes that shouldn't be related are not common.
We'll learn more about how classes relate to each other in the Classes
chapter.
Reflection
OOP programmers are accustomed to being able to query the type of
any value, even a generic one:
// C#
static void PrintType<T>() {
Console.WriteLine(typeof(T).Name);
}
This is an overview, from here you should read through the handbook
or explore the Playground examples
Congratulations on choosing TypeScript as one of your first languages
— you're already making good decisions!
More than this, JS has become popular enough to be used outside the
context of browsers, such as implementing JS servers using node.js.
The "run anywhere" nature of JS makes it an attractive choice for
cross-platform development. There are many developers these days
that use only JavaScript to program their entire stack!
#### Syntax
```ts twoslash
// @errors: 1005
let a = (4
As another example, this is JavaScript code that you can run in your
browser, and it will print a value:
console.log(4 / []);
Runtime Behavior
TypeScript is also a programming language that preserves the runtime
behavior of JavaScript. For example, dividing by zero in JavaScript
produces Infinity instead of throwing a runtime exception. As a
principle, TypeScript never changes the runtime behavior of JavaScript
code.
Erased Types
Roughly speaking, once TypeScript's compiler is done with checking your
code, it erases the types to produce the resulting "compiled" code.
This means that once your code is compiled, the resulting plain JS
code has no type information.
This also means that TypeScript never changes the behavior of your
program based on the types it inferred. The bottom line is that while
you might see type errors during compilation, the type system itself
has no bearing on how your program works when it runs.
If you find yourself searching for something like "how to sort a list in
TypeScript", remember: TypeScript is JavaScript's runtime with a
compile-time type checker. The way you sort a list in TypeScript is
the same way you do so in JavaScript. If you find a resource that
uses TypeScript directly, that's great too, but don't limit yourself to
thinking you need TypeScript-specific answers for everyday questions
about how to accomplish runtime tasks.
Check out the quick start guide here to start learning Angular now!
Install ASP.NET Core and
TypeScript
First, install ASP.NET Core if you need it. This quick-start guide
requires Visual Studio 2015 or 2017.
Next, if your version of Visual Studio does not already have the latest
TypeScript, you can install it.
Create a new project
1. Choose File
2. Choose New Project (Ctrl + Shift + N)
3. Search for .NET Core in the project search bar
4. Select ASP.NET Core Web Application and press the Next button
1. Name your project and solution. After select the Create button
1. In the last window, select the Empty template and press the
Create button
Open up your Startup.cs file and edit your Configure function to look
like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
}
You may need to restart VS for the red squiggly lines below
UseDefaultFiles and UseStaticFiles to disappear.
Add TypeScript
Next we will add a new folder and call it scripts .
The Path of "Add" then "New Folder" in Visual Studio from a Web
Project
Add TypeScript code
Right click on scripts and click New Item. Then choose TypeScript
File and name the file app.ts
function sayHello() {
const compiler = (document.getElementById("compiler") as HTMLInputElement)
.value;
const framework = (document.getElementById("framework") as HTMLInputElement)
.value;
return `Hello from ${compiler} and ${framework}!`;
}
Set up the build
Configure the TypeScript compiler
and click New Item. Then choose TypeScript Configuration File and
use the default name of tsconfig.json
`"noImplicitAny"` is good idea whenever you’re writing new code — you can make sure
that you don’t write any untyped code by mistake. `"compileOnSave"` makes it easy
to update your code in a running web app.
We need to setup NPM so that JavaScript packages can be downloaded. Right click on
the project and select **New Item**. Then choose **NPM Configuration File** and use
the default name of `package.json`.
![Screenshot of VS showing new file dialog with 'npm configuration file' selected]
(/images/tutorials/aspnet/packagejson.png)
Inside the `"devDependencies"` section of the `package.json` file, add _gulp_ and
_del_
```json tsconfig
"devDependencies": {
"gulp": "4.0.2",
"del": "5.1.0"
}
Visual Studio should start installing gulp and del as soon as you save
the file. If not, right-click package.json and then Restore Packages.
Set up gulp
Right click on the project and click New Item. Then choose
JavaScript File and use the name of gulpfile.js
/// <binding AfterBuild='default' Clean='clean' />
/*
This file is the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/
var paths = {
scripts: ["scripts/**/*.js", "scripts/**/*.ts", "scripts/**/*.map"],
};
gulp.task("clean", function () {
return del(["wwwroot/scripts/**/*"]);
});
gulp.task("default", function () {
gulp.src(paths.scripts).pipe(gulp.dest("wwwroot/scripts"));
});
The first line tells Visual Studio to run the task ‘default’ after the
build finishes. It will also run the ‘clean’ task when you ask Visual
Studio to clean the build.
If ‘default’ and ‘clean’ tasks don’t show up, refresh the explorer:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="scripts/app.js"></script>
<title></title>
</head>
<body>
<div id="message"></div>
<div>
Compiler: <input id="compiler" value="TypeScript"
onkeyup="document.getElementById('message').innerText = sayHello()" /><br />
Framework: <input id="framework" value="ASP.NET"
onkeyup="document.getElementById('message').innerText = sayHello()" />
</div>
</body>
</html>
Test
1. Run the project
2. As you type on the boxes you should see the message
appear/change!
Debug
1. In Edge, press F12 and click the Debugger tab.
2. Look in the first localhost folder, then scripts/app.ts
3. Put a breakpoint on the line with return.
4. Type in the boxes and confirm that the breakpoint hits in
TypeScript code and that inspection works correctly.
An image showing the debugger running the code you have just wrote
Congrats you've built your own .NET Core project with a TypeScript
frontend.
Babel vs tsc for TypeScript
When making a modern JavaScript project, you might ask yourself what
is the right way to convert files from TypeScript to JavaScript?
A lot of the time the answer is "it depends", or "someone may have
decided for you" depending on the project. If you are building your
project with an existing framework like tsdx, Angular, NestJS or any
framework mentioned in the Getting Started then this decision is
handled for you.
Is your build output mostly the same as your source input files?
Use tsc
By using babel's support for TypeScript, you get the ability to work
with existing build pipelines and are more likely to have a faster JS
emit time because Babel does not type check your code.
json tsconfig "compilerOptions": { // Ensure that .d.ts files are created by tsc, but
not .js files "declaration": true, "emitDeclarationOnly": true, // Ensure that Babel
can safely transpile files in the TypeScript project "isolatedModules": true }
For more information on these flags:
isolatedModules
declaration , emitDeclarationOnly
DOM Manipulation
You can explore the source code for the DOM type definitions
Basic Example
Given a simplified index.html file:
<!DOCTYPE html>
<html lang="en">
<head><title>TypeScript Dom Manipulation</title></head>
<body>
<div id="app"></div>
<!-- Assume index.js is the compiled output of index.ts -->
<script src="index.js"></script>
</body>
</html>
After compiling and running the index.html page, the resulting HTML
will be:
<div id="app">
<p>Hello, World!</p>
</div>
The Document Interface
The first line of the TypeScript code uses a global variable document .
Inspecting the variable shows it is defined by the Document interface
from the lib.dom.d.ts file. The code snippet contains calls to two
methods, getElementById and createElement .
Document.getElementById
The definition for this method is as follows:
Document.createElement
The definition for this method is (I have omitted the deprecated
definition):
For those interested, you can interact with custom tag elements
using the document.getElementsByTagName
interface HTMLElementTagNameMap {
"a": HTMLAnchorElement;
"abbr": HTMLElement;
"address": HTMLElement;
"applet": HTMLAppletElement;
"area": HTMLAreaElement;
...
}
Some elements do not exhibit unique properties and so they just return
HTMLElement , but other types do have unique properties and methods so
they return their specific interface (which will extend from or
implement HTMLElement ).
Node.appendChild
The last line of the code snippet is app?.appendChild(p) . The previous,
document.getElementById , section detailed that the optional chaining
operator is used here because app can potentially be null at runtime.
The appendChild method is defined by:
<div>
<p>Hello, World</p>
<p>TypeScript!</p>
</div>;
div.children;
// HTMLCollection(2) [p, p]
div.childNodes;
// NodeList(2) [p, p]
After capturing the div element, the children prop will return a
HTMLCollection list containing the HTMLParagraphElements . The childNodes
property will return a similar NodeList list of nodes. Each p tag will
still be of type HTMLParagraphElements , but the NodeList can contain
additional HTML nodes that the HTMLCollection list cannot.
Modify the html by removing one of the p tags, but keep the text.
<div>
<p>Hello, World</p>
TypeScript!
</div>;
div.children;
// HTMLCollection(1) [p]
div.childNodes;
// NodeList(2) [p, text]
See how both lists change. children now only contains the <p>Hello,
World</p> element, and the childNodes contains a text node rather than
two p nodes. The text part of the NodeList is the literal Node
containing the text TypeScript! . The children list does not contain this
Node because it is not considered an HTMLElement .
The querySelector and
querySelectorAll methods
Both of these methods are great tools for getting lists of dom
elements that fit a more unique set of constraints. They are defined in
lib.dom.d.ts as:
/**
* Returns the first element that is a descendant of node that matches selectors.
*/
querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K):
HTMLElementTagNameMap[K] | null;
querySelector<K extends keyof SVGElementTagNameMap>(selectors: K):
SVGElementTagNameMap[K] | null;
querySelector<E extends Element = Element>(selectors: string): E | null;
/**
* Returns all element descendants of node that match selectors.
*/
querySelectorAll<K extends keyof HTMLElementTagNameMap>(selectors: K):
NodeListOf<HTMLElementTagNameMap[K]>;
querySelectorAll<K extends keyof SVGElementTagNameMap>(selectors: K):
NodeListOf<SVGElementTagNameMap[K]>;
querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;
=> void) , and numeric indexing. Additionally, this method returns a list
of elements, not nodes, which is what NodeList was returning from the
.childNodes method. While this may appear as a discrepancy, take note
that interface Element extends from Node .
Sources:
ECMA-262 Standard
Introduction to the DOM
This quick start guide will teach you how to build TypeScript with gulp
and then add Browserify, uglify, or Watchify to the gulp pipeline. This
guide also shows how to add Babel functionality using Babelify.
mkdir proj
cd proj
proj/
├─ src/
└─ dist/
TypeScript files will start out in your src folder, run through the
TypeScript compiler and end up in dist .
mkdir src
mkdir dist
Initialize the project
Now we'll turn this folder into an npm package.
npm init
You'll be given a series of prompts. You can use the defaults except
for your entry point. For your entry point, use ./dist/main.js . You
can always go back and change these in the package.json file that's
been generated for you.
Install our dependencies
Now we can use npm install to install packages. First install gulp-cli
globally (if you use a Unix system, you may need to prefix the npm
## Create a `gulpfile.js`
```js
var gulp = require("gulp");
var ts = require("gulp-typescript");
var tsProject = ts.createProject("tsconfig.json");
gulp.task("default", function () {
return tsProject.src().pipe(tsProject()).js.pipe(gulp.dest("dist"));
});
Test the resulting app
gulp
node dist/main.js
console.log(sayHello("TypeScript"));
Make sure that the modules work by running `gulp` and then testing in Node:
```shell
gulp
node dist/main.js
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
</head>
<body>
<p id="greeting">Loading ...</p>
<script src="bundle.js"></script>
</body>
</html>
showHello("greeting", "TypeScript");
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
gulp.task(
"default",
gulp.series(gulp.parallel("copy-html"), function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
})
.plugin(tsify)
.bundle()
.pipe(source("bundle.js"))
.pipe(gulp.dest("dist"));
})
);
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
function bundle() {
return watchedBrowserify
.bundle()
.on("error", fancy_log)
.pipe(source("bundle.js"))
.pipe(gulp.dest("dist"));
}
There are basically three changes here, but they require you to
refactor your code a bit.
Together (1) and (2) mean that we have to move our call to
browserify out of the default task. And we have to give the function
for default a name since both Watchify and Gulp need to call it.
Adding logging with (3) is optional but very useful for debugging
your setup.
Now when you run Gulp, it should start and stay running. Try changing
the code for showHello in main.ts and saving it. You should see
output that looks like this:
proj$ gulp
[10:34:20] Using gulpfile ~/src/proj/gulpfile.js
[10:34:20] Starting 'copy-html'...
[10:34:20] Finished 'copy-html' after 26 ms
[10:34:20] Starting 'default'...
[10:34:21] 2824 bytes written (0.13 seconds)
[10:34:21] Finished 'default' after 1.36 s
[10:35:22] 2261 bytes written (0.02 seconds)
[10:35:24] 2808 bytes written (0.05 seconds)
Uglify
First install Uglify. Since the point of Uglify is to mangle your code,
we also need to install vinyl-buffer and gulp-sourcemaps to keep
sourcemaps working.
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
gulp.task(
"default",
gulp.series(gulp.parallel("copy-html"), function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
})
.plugin(tsify)
.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(sourcemaps.write("./"))
.pipe(gulp.dest("dist"));
})
);
Notice that uglify itself has just one call — the calls to buffer and
sourcemaps exist to make sure sourcemaps keep working. These calls
give us a separate sourcemap file instead of using inline sourcemaps
like before. Now you can run Gulp and check that bundle.js does get
minified into an unreadable mess:
gulp
cat dist/bundle.js
Babel
First install Babelify and the Babel preset for ES2015. Like Uglify,
Babelify mangles code, so we'll need vinyl-buffer and gulp-
sourcemaps. By default Babelify will only process files with extensions
of .js , .es , .es6 and .jsx so we need to add the .ts extension
as an option to Babelify.
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
gulp.task(
"default",
gulp.series(gulp.parallel("copy-html"), function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
})
.plugin(tsify)
.transform("babelify", {
presets: ["es2015"],
extensions: [".ts"],
})
.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(sourcemaps.write("./"))
.pipe(gulp.dest("dist"));
})
);
If that's the case, the files that you've written are going to be used
as inputs to TypeScript, and you'll run the outputs it produces.
During our JS to TS migration, we'll need to separate our input files
to prevent TypeScript from overwriting them. If your output files
need to reside in a specific directory, then that will be your output
directory.
From this point on, we're going to assume that your directory is set
up something like this:
projectRoot
├── src
│ ├── file1.js
│ └── file2.js
├── built
└── tsconfig.json
If you have a tests folder outside of your src directory, you might
have one tsconfig.json in src , and one in tests as well.
Writing a Configuration File
TypeScript uses a file called tsconfig.json for managing your project's
options, such as which files you want to include, and what sorts of
checking you want to perform. Let's create a bare-bones one for our
project:
{
"compilerOptions": {
"outDir": "./built",
"allowJs": true,
"target": "es5"
},
"include": ["./src/**/*"]
}
At this point, if you try running tsc at the root of your project,
you should see output files in the built directory. The layout of files
in built should look identical to the layout of src . You should now
have TypeScript working with your project.
Early Benefits
Even at this point you can get some great benefits from TypeScript
understanding your project. If you open up an editor like VS Code or
Visual Studio, you'll see that you can often get some tooling support
like completion. You can also catch certain bugs with options like:
TypeScript will also warn about unreachable code and labels, which you
can disable with allowUnreachableCode and allowUnusedLabels respectively.
Integrating with Build Tools
You might have some more build steps in your pipeline. Perhaps you
concatenate something to each of your files. Each build tool is
different, but we'll do our best to cover the gist of things.
Gulp
If you're using Gulp in some fashion, we have a tutorial on using Gulp
with TypeScript, and integrating with common build tools like
Browserify, Babelify, and Uglify. You can read more there.
Webpack
Webpack integration is pretty simple. You can use ts-loader , a
TypeScript loader, combined with source-map-loader for easier
debugging. Simply run
file:
module.exports = {
entry: "./src/index.ts",
output: {
filename: "./dist/bundle.js",
},
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"],
},
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'.
{ test: /\.tsx?$/, loader: "ts-loader" },
// All output '.js' files will have any sourcemaps re-processed by 'source-
map-loader'.
{ test: /\.js$/, loader: "source-map-loader" },
],
},
// Other options...
};
It's important to note that ts-loader will need to run before any
other loader that deals with .js files.
You can see an example of using Webpack in our tutorial on React and
Webpack.
Moving to TypeScript Files
At this point, you're probably ready to start using TypeScript files.
The first step is to rename one of your .js files to .ts . If your
file uses JSX, you'll need to rename it to .tsx .
Finished with that step? Great! You've successfully migrated a file from
JavaScript to TypeScript!
Of course, that might not feel right. If you open that file in an
editor with TypeScript support (or if you run tsc --pretty ), you might
see red squiggles on certain lines. You should think of these the same
way you'd think of red squiggles in an editor like Microsoft Word.
TypeScript will still translate your code, just like Word will still let you
print your documents.
If that sounds too lax for you, you can tighten that behavior up. If,
for instance, you don't want TypeScript to compile to JavaScript in
the face of errors, you can use the noEmitOnError option. In that
sense, TypeScript has a dial on its strictness, and you can turn that
knob up as high as you want.
If you plan on using the stricter settings that are available, it's best
to turn them on now (see Getting Stricter Checks below). For
instance, if you never want TypeScript to silently infer any for a
type without you explicitly saying so, you can use noImplicitAny before
you start modifying your files. While it might feel somewhat
overwhelming, the long-term gains become apparent much more quickly.
Weeding out Errors
Like we mentioned, it's not unexpected to get error messages after
conversion. The important thing is to actually go one by one through
these and decide how to deal with the errors. Often these will be
legitimate bugs, but sometimes you'll have to explain what you're
trying to do a little better to TypeScript.
'require'. , and Cannot find name 'define'. . In these cases, it's likely that
you're using modules. While you can just convince TypeScript that
these exist by writing out
// For Node/CommonJS
declare function require(path: string): any;
or
// For RequireJS/AMD
declare function define(...args: any[]): any;
it's better to get rid of those calls and use TypeScript syntax for
imports.
foo.doStuff();
foo.doStuff();
After that, you'll be able to import lodash with no issues, and get
accurate completions.
function foo() {
// ...
}
module.exports = foo;
function foo() {
// ...
}
export = foo;
function myCoolFunction() {
if (arguments.length == 2 && !Array.isArray(arguments[1])) {
var f = arguments[0];
var arr = arguments[1];
// ...
}
// ...
}
myCoolFunction(
function (x) {
console.log(x);
},
[1, 2, 3, 4]
);
myCoolFunction(
function (x) {
console.log(x);
},
1,
2,
3,
4
);
TypeScript will say that you can't assign to color and volume because
it first figured out the type of options as {} which doesn't have any
properties. If you instead moved the declarations into the object
literal themselves, you'd get no errors:
let options = {
color: "red",
volume: 11,
};
You could also define the type of options and add a type assertion
on the object literal.
interface Options {
color: string;
volume: number;
}
For instance, if you have something that's typed as Object you won't
be able to call methods like toLowerCase() on it. Being more general
usually means you can do less with a type, but any is special in that it
is the most general type while still allowing you to do anything with it.
That means you can call it, construct it, access properties on it, etc.
Keep in mind though, whenever you use any , you lose out on most of
the error checking and editor support that TypeScript gives you.
No Implicit any
There are certain cases where TypeScript can't figure out what certain
types should be. To be as lenient as possible, it will decide to use the
type any in its place. While this is great for migration, using any
means that you're not getting any type safety, and you won't get the
same tooling support you'd get elsewhere. You can tell TypeScript to
flag these locations down and give an error with the noImplicitAny
option.
class Point {
constructor(public x, public y) {}
getDistance(p: Point) {
let dx = p.x - this.x;
let dy = p.y - this.y;
return Math.sqrt(dx ** 2 + dy ** 2);
}
}
// ...
This has the same problems we mentioned above - we could easily have
misspelled getDistance and not gotten an error. For this reason,
TypeScript has the noImplicitThis option. When that option is set,
TypeScript will issue an error when this is used without an explicit (or
inferred) type. The fix is to use a this -parameter to give an
explicit type in the interface or in the function itself:
All of these are great starting points. We use Gatsby with TypeScript
for this website, so that can also be a useful reference
implementation.
Documentation
Here are some of the best places to find up-to-date information on
React and TypeScript:
Visual Studio 2017 and Visual Studio 2015 Update 3 include TypeScript
by default. If you didn't install TypeScript with Visual Studio, you can
still download it.
document.body.textContent = greeter(user);
```shell
tsc greeter.ts
The result will be a file greeter.js which contains the same JavaScript
that you fed in. We're up and running using TypeScript in our
JavaScript app!
document.body.textContent = greeter(user);
## Type annotations
Type annotations in TypeScript are lightweight ways to record the intended contract
of the function or variable.
In this case, we intend the greeter function to be called with a single string
parameter.
We can try changing the call greeter to pass an array instead:
```ts twoslash
// @errors: 2345
function greeter(person: string) {
return "Hello, " + person;
}
document.body.textContent = greeter(user);
Notice that although there were errors, the greeter.js file is still
created. You can use TypeScript even if there are errors in your
code. But in this case, TypeScript is warning that your code will likely
not run as expected.
Interfaces
Let's develop our sample further. Here we use an interface that
describes objects that have a firstName and lastName field. In
TypeScript, two types are compatible if their internal structure is
compatible. This allows us to implement an interface just by having the
shape the interface requires, without an explicit implements clause.
document.body.textContent = greeter(user);
## Classes
Finally, let's extend the example one last time with classes.
TypeScript supports new features in JavaScript, like support for class-based
object-oriented programming.
Here we're going to create a `Student` class with a constructor and a few public
fields.
Notice that classes and interfaces play well together, letting the programmer
decide on the right level of abstraction.
```ts twoslash
class Student {
fullName: string;
constructor(
public firstName: string,
public middleInitial: string,
public lastName: string
) {
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
document.body.textContent = greeter(user);
Re-run tsc greeter.ts and you'll see the generated JavaScript is the
same as the earlier code. Classes in TypeScript are just a shorthand
for the same prototype-based OO that is frequently used in
JavaScript.
Running your TypeScript web
app
Now type the following in greeter.html :
<!DOCTYPE html>
<html>
<head>
<title>TypeScript Greeter</title>
</head>
<body>
<script src="greeter.js"></script>
</body>
</html>
Optional: Open greeter.ts in Visual Studio, or copy the code into the
TypeScript playground. You can hover over identifiers to see their
types. Notice that in some cases these types are inferred automatically
for you. Re-type the last line, and see completion lists and parameter
help based on the types of the DOM elements. Put your cursor on the
reference to the greeter function, and hit F12 to go to its definition.
Notice, too, that you can right-click on a symbol and use refactoring
to rename it.
The type information provided works together with the tools to work
with JavaScript at application scale. For more examples of what's
possible in TypeScript, see the Samples section of the website.
The Handbook
Non-Goals
The Handbook is also intended to be a concise document that can be
comfortably read in a few hours. Certain topics won't be covered in
order to keep things short.
Finally, the Handbook won't cover how TypeScript interacts with other
tools, except where necessary. Topics like how to configure TypeScript
with webpack, rollup, parcel, react, babel, closure, lerna, rush, bazel,
preact, vue, angular, svelte, jquery, yarn, or npm are out of scope -
you can find these resources elsewhere on the web.
Get Started
Before getting started with Basic Types, we recommend reading one
of the following introductory pages. These introductions are intended
to highlight key similarities and differences between TypeScript and
your favored programming language, and clear up common
misconceptions specific to those languages.
## Number
```ts twoslash
// @target: ES2020
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
String
Another fundamental part of creating programs in JavaScript for
webpages and servers alike is working with textual data. As in other
languages, we use the type string to refer to these textual
datatypes. Just like JavaScript, TypeScript also uses double quotes
( " ) or single quotes ( ' ) to surround string data.
You can also use _template strings_, which can span multiple lines and have
embedded expressions.
These strings are surrounded by the backtick/backquote (`` ` ``) character, and
embedded expressions are of the form `${ expr }`.
```ts twoslash
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.
``ts twoslash let fullName: string = Bob Bobbington`; let age: number = 37;
// ---cut--- let sentence: string = "Hello, my name is " +
fullName + ".\n\n" + "I'll be " + (age + 1) + " years old next
month.";
## Array
```ts twoslash
let list: number[] = [1, 2, 3];
## Tuple
Tuple types allow you to express an array with a fixed number of elements whose
types are known, but need not be the same. For example, you may want to represent a
value as a pair of a `string` and a `number`:
```ts twoslash
// @errors: 2322
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error
console.log(x[1].substring(1));
Accessing an element outside the set of known indices fails with an error:
```ts twoslash
// @errors: 2493 2532 2322
let x: [string, number];
x = ["hello", 10]; // OK
/// ---cut---
x[3] = "world";
console.log(x[5].toString());
Enum
A helpful addition to the standard set of datatypes from JavaScript is
the enum . As in languages like C#, an enum is a way of giving more
friendly names to sets of numeric values.
```ts twoslash
enum Color {
Red = 1,
Green,
Blue,
}
let c: Color = Color.Green;
```ts twoslash
enum Color {
Red = 1,
Green,
Blue,
}
let colorName: string = Color[2];
// Displays 'Green'
console.log(colorName);
Unknown
We may need to describe the type of variables that we do not know
when we are writing an application. These values may come from
dynamic content – e.g. from the user – or we may want to
intentionally accept all values in our API. In these cases, we want to
provide a type that tells the compiler and future readers that this
variable could be anything, so we give it the unknown type.
If you have a variable with an unknown type, you can narrow it to something more
specific by doing `typeof` checks, comparison checks, or more advanced type guards
that will be discussed in a later chapter:
```ts twoslash
// @errors: 2322 2322 2322
declare const maybe: unknown;
// 'maybe' could be a string, object, boolean, undefined, or other types
const aNumber: number = maybe;
The `any` type is a powerful way to work with existing JavaScript, allowing you to
gradually opt-in and opt-out of type checking during compilation.
Unlike `unknown`, variables of type `any` allow you to access arbitrary properties,
even ones that don't exist.
These properties include functions and TypeScript will not check their existence or
type:
```ts twoslash
// @errors: 2571
let looselyTyped: any = 4;
// OK, ifItExists might exist at runtime
looselyTyped.ifItExists();
// OK, toFixed exists (but the compiler doesn't check)
looselyTyped.toFixed();
## Void
`void` is a little like the opposite of `any`: the absence of having any type at
all.
You may commonly see this as the return type of functions that do not return a
value:
```ts twoslash
function warnUser(): void {
console.log("This is my warning message");
}
Declaring variables of type void is not useful because you can only
assign null (only if --strictNullChecks is not specified, see next
section) or undefined to them:
In TypeScript, both `undefined` and `null` actually have their types named
`undefined` and `null` respectively.
Much like `void`, they're not extremely useful on their own:
```ts twoslash
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
By default null and undefined are subtypes of all other types. That
means you can assign null and undefined to something like number .
However, when using the --strictNullChecks flag, null and undefined
are only assignable to unknown , any and their respective types (the
one exception being that undefined is also assignable to void ). This
helps avoid many common errors. In cases where you want to pass in
either a string or null or undefined , you can use the union type
string | null | undefined .
Union types are an advanced topic that we'll cover in a later chapter.
The never type is a subtype of, and assignable to, every type;
however, no type is a subtype of, or assignable to, never (except
never itself). Even any isn't assignable to never .
`object` is a type that represents the non-primitive type, i.e. anything that is
not `number`, `string`, `boolean`, `bigint`, `symbol`, `null`, or `undefined`.
With `object` type, APIs like `Object.create` can be better represented. For
example:
```ts twoslash
// @errors: 2345
declare function create(o: object | null): void;
// OK
create({ prop: 0 });
create(null);
create(42);
create("string");
create(false);
create(undefined);
Type assertions are a way to tell the compiler "trust me, I know what
I'm doing." A type assertion is like a type cast in other languages,
but it performs no special checking or restructuring of data. It has
no runtime impact and is used purely by the compiler. TypeScript
assumes that you, the programmer, have performed any special checks
that you need.
```ts twoslash
let someValue: unknown = "this is a string";
The two samples are equivalent. Using one over the other is mostly a
choice of preference; however, when using TypeScript with JSX, only
as -style assertions are allowed.
A note about let
You may have noticed that so far, we've been using the let keyword
instead of JavaScript's var keyword which you might be more familiar
with. The let keyword is actually a newer JavaScript construct that
TypeScript makes available. You can read in the Handbook Reference on
Variable Declarations more about how let and const fix a lot of the
problems with var .
About Number , String ,
Boolean , Symbol and Object
It can be tempting to think that the types Number , String , Boolean ,
Symbol , or Object are the same as the lowercase versions
recommended above. These types do not refer to the language
primitives however, and almost never should be used as a type.
reverse("hello world");
Instead, use the types `number`, `string`, `boolean`, `object` and `symbol`.
```ts twoslash
function reverse(s: string): string {
return s.split("").reverse().join("");
}
reverse("hello world");
One of TypeScript's core principles is that type checking focuses on
the shape that values have. This is sometimes called "duck typing" or
"structural subtyping". In TypeScript, interfaces fill the role of naming
these types, and are a powerful way of defining contracts within your
code as well as contracts with code outside of your project.
Our First Interface
The easiest way to see how interfaces work is to start with a simple
example:
We can write the same example again, this time using an interface to describe the
requirement of having the `label` property that is a string:
```ts twoslash
interface LabeledValue {
label: string;
}
It's worth pointing out that the type checker does not require that
these properties come in any sort of order, only that the properties
the interface requires are present and have the required type.
Optional Properties
Not all properties of an interface may be required. Some exist under
certain conditions or may not be there at all. These optional
properties are popular when creating patterns like "option bags" where
you pass an object to a function that only has a couple of properties
filled in.
The advantage of optional properties is that you can describe these possibly
available properties while still also preventing use of properties that are not
part of the interface.
For example, had we mistyped the name of the `color` property in `createSquare`, we
would get an error message letting us know:
```ts twoslash
// @errors: 2551
interface SquareConfig {
color?: string;
width?: number;
}
```ts twoslash
// @errors: 2540
interface Point {
readonly x: number;
readonly y: number;
}
// ---cut---
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
```ts twoslash // @errors: 2542 2339 2540 4104 let a: number[] = [1,
2, 3, 4]; let ro: ReadonlyArray = a;
```ts twoslash
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
a = ro as number[];
readonly vs const
The easiest way to remember whether to use readonly or const is to
ask whether you're using it on a variable or a property. Variables use
const whereas properties use readonly .
Excess Property Checks
In our first example using interfaces, TypeScript lets us pass { size:
However, combining the two naively would allow an error to sneak in.
For example, taking our last example using createSquare :
You could argue that this program is correctly typed, since the `width` properties
are compatible, there's no `color` property present, and the extra `colour`
property is insignificant.
However, TypeScript takes the stance that there's probably a bug in this code.
Object literals get special treatment and undergo _excess property checking_ when
assigning them to other variables, or passing them as arguments.
If an object literal has any properties that the "target type" doesn't have, you'll
get an error:
```ts twoslash
// @errors: 2345 2739
interface SquareConfig {
color?: string;
width?: number;
}
```ts twoslash
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
One final way to get around these checks, which might be a bit
surprising, is to assign the object to another variable: Since
squareOptions won't undergo excess property checks, the compiler
won't give you an error.
```ts twoslash
// @errors: 2559
interface SquareConfig {
color?: string;
width?: number;
}
Keep in mind that for simple code like above, you probably shouldn't
be trying to "get around" these checks. For more complex object
literals that have methods and hold state, you might need to keep
these techniques in mind, but a majority of excess property errors are
actually bugs. That means if you're running into excess property
checking problems for something like option bags, you might need to
revise some of your type declarations. In this instance, if it's okay to
pass an object with both a color or colour property to createSquare ,
you should fix up the definition of SquareConfig to reflect that.
Function Types
Interfaces are capable of describing the wide range of shapes that
JavaScript objects can take. In addition to describing an object with
properties, interfaces are also capable of describing function types.
Once defined, we can use this function type interface like we would other
interfaces.
Here, we show how you can create a variable of a function type and assign it a
function value of the same type.
```ts twoslash
interface SearchFunc {
(source: string, subString: string): boolean;
}
// ---cut---
let mySearch: SearchFunc;
Function parameters are checked one at a time, with the type in each corresponding
parameter position checked against each other.
If you do not want to specify types at all, TypeScript's contextual typing can
infer the argument types since the function value is assigned directly to a
variable of type `SearchFunc`.
Here, also, the return type of our function expression is implied by the values it
returns (here `false` and `true`).
```ts twoslash
interface SearchFunc {
(source: string, subString: string): boolean;
}
// ---cut---
let mySearch: SearchFunc;
Similarly to how we can use interfaces to describe function types, we can also
describe types that we can "index into" like `a[10]`, or `ageMap["daniel"]`.
Indexable types have an _index signature_ that describes the types we can use to
index into the object, along with the corresponding return types when indexing.
Let's take an example:
```ts twoslash
interface StringArray {
[index: number]: string;
}
}
While string index signatures are a powerful way to describe the "dictionary"
pattern, they also enforce that all properties match their return type.
This is because a string index declares that `obj.property` is also available as
`obj["property"]`.
In the following example, `name`'s type does not match the string index's type, and
the type checker gives an error:
```ts twoslash
// @errors: 2411
interface NumberDictionary {
[index: string]: number;
length: number; // ok, length is a number
name: string; // error, the type of 'name' is not a subtype of the indexer
}
Finally, you can make index signatures `readonly` in order to prevent assignment to
their indices:
```ts twoslash
// @errors: 2542
interface ReadonlyStringArray {
readonly [index: number]: string;
}
Implementing an interface
One of the most common uses of interfaces in languages like C# and
Java, that of explicitly enforcing that a class meets a particular
contract, is also possible in TypeScript.
You can also describe methods in an interface that are implemented in the class, as
we do with `setTime` in the below example:
```ts twoslash
// @strictPropertyInitialization: false
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
Interfaces describe the public side of the class, rather than both the
public and private side. This prohibits you from using them to check
that a class also has particular types for the private side of the class
instance.
ff t t t t d
Difference between the static and
instance sides of classes
When working with classes and interfaces, it helps to keep in mind that
a class has two types: the type of the static side and the type of the
instance side. You may notice that if you create an interface with a
construct signature and try to create a class that implements this
interface you get an error:
Instead, you would need to work with the static side of the class directly.
In this example, we define two interfaces, `ClockConstructor` for the constructor
and `ClockInterface` for the instance methods.
Then, for convenience, we define a constructor function `createClock` that creates
instances of the type that is passed to it:
```ts twoslash
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
function createClock(
ctor: ClockConstructor,
hour: number,
minute: number
): ClockInterface {
return new ctor(hour, minute);
}
## Extending Interfaces
```ts twoslash
interface Shape {
color: string;
}
## Hybrid Types
As we mentioned earlier, interfaces can describe the rich types present in real
world JavaScript.
Because of JavaScript's dynamic and flexible nature, you may occasionally encounter
an object that works as a combination of some of the types described above.
One such example is an object that acts as both a function and an object, with
additional properties:
```ts twoslash
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
This is useful when you have a large inheritance hierarchy, but want to
specify that your code works with only subclasses that have certain
properties. The subclasses don't have to be related besides inheriting
from the base class. For example:
```ts twoslash
// @strict: false
let z = 100;
function addToZ(x, y) {
return x + y + z;
}
Function Types
We can add types to each of the parameters and then to the function itself to add a
return type.
TypeScript can figure the return type out by looking at the return statements, so
we can also optionally leave this off in many cases.
Now that we've typed the function, let's write the full type of the function out by
looking at each piece of the function type.
```ts twoslash
let myAdd: (x: number, y: number) => number = function (
x: number,
y: number
): number {
return x + y;
};
A function's type has the same two parts: the type of the arguments
and the return type. When writing out the whole function type, both
parts are required. We write out the parameter types just like a
parameter list, giving each parameter a name and a type. This name is
just to help with readability. We could have instead written:
```ts twoslash let myAdd: (baseValue: number, increment: number) =>
number = function ( x: number, y: number ): number { return x + y;
};
As long as the parameter types line up, it's considered a valid type for the
function, regardless of the names you give the parameters in the function type.
Of note, only the parameters and the return type make up the function type.
Captured variables are not reflected in the type.
In effect, captured variables are part of the "hidden state" of any function and do
not make up its API.
In playing with the example, you may notice that the TypeScript compiler can figure
out the type even if you only have types on one side of the equation:
```ts twoslash
// The parameters 'x' and 'y' have the type number
let myAdd = function (x: number, y: number): number {
return x + y;
};
In JavaScript, every parameter is optional, and users may leave them off as they
see fit.
When they do, their value is `undefined`.
We can get this functionality in TypeScript by adding a `?` to the end of
parameters we want to be optional.
For example, let's say we want the last name parameter from above to be optional:
```ts twoslash
// @errors: 2554
function buildName(firstName: string, lastName?: string) {
if (lastName) return firstName + " " + lastName;
else return firstName;
}
Default-initialized parameters that come after all required parameters are treated
as optional, and just like optional parameters, can be omitted when calling their
respective function.
This means optional parameters and trailing default parameters will share
commonality in their types, so both
```ts
function buildName(firstName: string, lastName?: string) {
// ...
}
and
share the same type (firstName: string, lastName?: string) => string . The
default value of lastName disappears in the type, only leaving behind
the fact that the parameter is optional.
Unlike plain optional parameters, default-initialized parameters don't
need to occur after required parameters. If a default-initialized
parameter comes before a required parameter, users need to explicitly
pass undefined to get the default initialized value. For example, we
could write our last example with only a default initializer on firstName :
## Rest Parameters
Required, optional, and default parameters all have one thing in common: they talk
about one parameter at a time.
Sometimes, you want to work with multiple parameters as a group, or you may not
know how many parameters a function will ultimately take.
In JavaScript, you can work with the arguments directly using the `arguments`
variable that is visible inside every function body.
```ts twoslash
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
```ts twoslash
// @strict: false
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function () {
return function () {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
}, };
## `this` parameters
```ts
function f(this: void) {
// make sure `this` is unusable in this standalone function
}
}, };
You can also run into errors with `this` in callbacks, when you pass functions to a
library that will later call them.
Because the library that calls your callback will call it like a normal function,
`this` will be `undefined`.
With some work you can use `this` parameters to prevent errors with callbacks too.
First, the library author needs to annotate the callback type with `this`:
```ts twoslash
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
```ts twoslash
// @strict: false
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
interface Event {
message: string;
}
declare const uiElement: UIElement;
// ---cut---
class Handler {
info: string;
onClickGood(this: void, e: Event) {
// can't use `this` here because it's of type void!
console.log("clicked!");
}
}
## Overloads
```ts twoslash
// @strict: false
let suits = ["hearts", "spades", "clubs", "diamonds"];
let myDeck = [
{ suit: "diamonds", card: 2 },
{ suit: "spades", card: 10 },
{ suit: "hearts", card: 4 },
];
The answer is to supply multiple function types for the same function
as a list of overloads. This list is what the compiler will use to resolve
function calls. Let's create a list of overloads that describe what our
pickCard accepts and what it returns.
With this change, the overloads now give us type checked calls to the
pickCard function.
In order for the compiler to pick the correct type check, it follows a
similar process to the underlying JavaScript. It looks at the overload
list and, proceeding with the first overload, attempts to call the
function with the provided parameters. If it finds a match, it picks
this overload as the correct overload. For this reason, it's customary
to order overloads from most specific to least specific.
Note that the function pickCard(x): any piece is not part of the overload
list, so it only has two overloads: one that takes an object and one
that takes a number. Calling pickCard with any other parameter types
would cause an error.
A literal is a more concrete sub-type of a collective type. What this
means is that "Hello World" is a string , but a string is not "Hello
// So, TypeScript sets the type to be "Hello World" not string const
helloWorld = "Hello World";
// On the other hand, a let can change, and so the compiler declares
it a string let hiWorld = "Hi World";
The process of going from an infinite number of potential cases (there are an
infinite number of possible string values) to a smaller, finite number of potential
case (in `helloWorld`'s case: 1) is called narrowing.
In practice string literal types combine nicely with union types, type guards, and
type aliases.
You can use these features together to get enum-like behavior with strings.
```ts twoslash
// @errors: 2345
type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
animate(dx: number, dy: number, easing: Easing) {
if (easing === "ease-in") {
// ...
} else if (easing === "ease-out") {
} else if (easing === "ease-in-out") {
} else {
// It's possible that someone could reach this
// by ignoring your types though.
}
}
}
You can pass any of the three allowed strings, but any other string
will give the error
```ts twoslash
/** Creates a map centered at loc/lat */
declare function setupMap(config: MapConfig): void;
// ---cut---
interface MapConfig {
lng: number;
lat: number;
tileSize: 8 | 16 | 32;
}
Intersection and Union types are one of the ways in which you can
compose types.
Union Types
Occasionally, you'll run into a library that expects a parameter to be
either a number or a string . For instance, take the following function:
'${typeof padding}'. ); }
The problem with `padLeft` in the above example is that its `padding` parameter is
typed as `any`.
That means that we can call it with an argument that's neither a `number` nor a
`string`, but TypeScript will be okay with it.
```ts twoslash
declare function padLeft(value: string, padding: any): string;
// ---cut---
// passes at compile time, fails at runtime.
let indentedString = padLeft("Hello world", true);
Instead of any , we can use a union type for the padding parameter:
If we have a value that is a union type, we can only access members that are common
to all types in the union.
```ts twoslash
// @errors: 2339
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
Union types can be a bit tricky here, but it just takes a bit of
intuition to get used to. If a value has the type A | B , we only know
for certain that it has members that both A and B have. In this
example, Bird has a member named fly . We can't be sure whether a
variable typed as Bird | Fish has a fly method. If the variable is
really a Fish at runtime, then calling pet.fly() will fail.
Discriminating Unions
A common technique for working with unions is to have a single field
which uses literal types which you can use to let TypeScript narrow
down the possible current type. For example, we're going to create a
union of three types which have a single shared field.
type NetworkLoadingState = {
state: "loading";
};
type NetworkFailedState = {
state: "failed";
code: number;
};
type NetworkSuccessState = {
state: "success";
response: {
title: string;
duration: number;
summary: string;
};
};
All of the above types have a field named state , and then they also
have their own fields:
code response
Given the state field is common in every type inside NetworkState - it
is safe for your code to access without an existence check.
With state as a literal type, you can compare the value of state to
the equivalent string and TypeScript will know which type is currently
being used.
In this case, you can use a switch statement to narrow down which
type is represented at runtime:
${state.response.summary} ; } }
## Union Exhaustiveness checking
We would like the compiler to tell us when we don't cover all variants of the
discriminated union.
For example, if we add `NetworkFromCachedState` to `NetworkState`, we need to
update `logger` as well:
```ts twoslash
// @errors: 2366
type NetworkLoadingState = { state: "loading" };
type NetworkFailedState = { state: "failed"; code: number };
type NetworkSuccessState = {
state: "success";
response: {
title: string;
duration: number;
summary: string;
};
};
// ---cut---
type NetworkFromCachedState = {
state: "from_cache";
id: string;
response: NetworkSuccessState["response"];
};
type NetworkState =
| NetworkLoadingState
| NetworkFailedState
| NetworkSuccessState
| NetworkFromCachedState;
The second method uses the `never` type that the compiler uses to check for
exhaustiveness:
```ts twoslash
// @errors: 2345
type NetworkLoadingState = { state: "loading" };
type NetworkFailedState = { state: "failed"; code: number };
type NetworkSuccessState = { state: "success" };
type NetworkFromCachedState = { state: "from_cache" };
type NetworkState =
| NetworkLoadingState
| NetworkFailedState
| NetworkSuccessState
| NetworkFromCachedState;
// ---cut---
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
and Loggable . That means an object of this type will have all members
of all three types.
console.log(response.artists); }; ```
Traditional JavaScript uses functions and prototype-based inheritance
to build up reusable components, but this may feel a bit awkward to
programmers more comfortable with an object-oriented approach,
where classes inherit functionality and objects are built from these
classes. Starting with ECMAScript 2015, also known as ECMAScript 6,
JavaScript programmers can build their applications using this object-
oriented class-based approach. In TypeScript, we allow developers to
use these techniques now, and compile them down to JavaScript that
works across all major browsers and platforms, without having to wait
for the next version of JavaScript.
Classes
Let's take a look at a simple class-based example:
You'll notice that in the class when we refer to one of the members of the class we
prepend `this.`.
This denotes that it's a member access.
In the last line we construct an instance of the `Greeter` class using `new`.
This calls into the constructor we defined earlier, creating a new object with the
`Greeter` shape, and running the constructor to initialize it.
## Inheritance
```ts twoslash
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
This example shows the most basic inheritance feature: classes inherit
properties and methods from base classes. Here, Dog is a derived
class that derives from the Animal base class using the extends
keyword. Derived classes are often called subclasses, and base classes
are often called superclasses.
Because Dog extends the functionality from Animal , we were able to
create an instance of Dog that could both bark() and move() .
let sam = new Snake("Sammy the Python"); let tom: Animal = new
Horse("Tommy the Palomino");
sam.move(); tom.move(34);
One difference from the prior example is that each derived class that contains a
constructor function _must_ call `super()` which will execute the constructor of
the base class.
What's more, before we _ever_ access a property on `this` in a constructor body, we
_have_ to call `super()`.
This is an important rule that TypeScript will enforce.
The example also shows how to override methods in the base class with methods that
are specialized for the subclass.
Here both `Snake` and `Horse` create a `move` method that overrides the `move` from
`Animal`, giving it functionality specific to each class.
Note that even though `tom` is declared as an `Animal`, since its value is a
`Horse`, calling `tom.move(34)` will call the overriding method in `Horse`:
Slithering… Sammy the Python moved 5m. Galloping… Tommy the Palomino
moved 34m.
## Public, private, and protected modifiers
## Public by default
In our examples, we've been able to freely access the members that we declared
throughout our programs.
If you're familiar with classes in other languages, you may have noticed in the
above examples we haven't had to use the word `public` to accomplish this; for
instance, C# requires that each member be explicitly labeled `public` to be
visible.
In TypeScript, each member is `public` by default.
```ts twoslash
class Animal {
public name: string;
new Animal("Cat").#name;
This syntax is built into the JavaScript runtime and can have better guarantees
about the isolation of each private field.
Right now, the best documentation for these private fields is in the TypeScript 3.8
[release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-3-
8-beta/#ecmascript-private-fields).
TypeScript also has its own way to declare a member as being marked `private`, it
cannot be accessed from outside of its containing class. For example:
```ts twoslash
// @errors: 2341
class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
new Animal("Cat").name;
Let's look at an example to better see how this plays out in practice:
let animal = new Animal("Goat"); let rhino = new Rhino(); let employee
= new Employee("Bob");
## Understanding `protected`
The `protected` modifier acts much like the `private` modifier with the exception
that members declared `protected` can also be accessed within deriving classes. For
example,
```ts twoslash
// @errors: 2445
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
A constructor may also be marked protected . This means that the class
cannot be instantiated outside of its containing class, but can be
extended. For example,
${this.department}. ; } }
## Readonly modifier
```ts twoslash
// @errors: 2540
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor(theName: string) {
this.name = theName;
}
}
## Accessors
```ts twoslash
// @strict: false
class Employee {
fullName: string;
}
if (employee.fullName) {
console.log(employee.fullName);
}
In this version, we add a setter that checks the length of the newName
this._fullName = newName;
} }
if (employee.fullName) { console.log(employee.fullName); }
To prove to ourselves that our accessor is now checking the length of values, we
can attempt to assign a name longer than 10 characters and verify that we get an
error.
First, accessors require you to set the compiler to output ECMAScript 5 or higher.
Downleveling to ECMAScript 3 is not supported.
Second, accessors with a `get` and no `set` are automatically inferred to be
`readonly`.
This is helpful when generating a `.d.ts` file from your code, because users of
your property can see that they can't change it.
## Static Properties
Up to this point, we've only talked about the _instance_ members of the class,
those that show up on the object when it's instantiated.
We can also create _static_ members of a class, those that are visible on the class
itself rather than on the instances.
In this example, we use `static` on the origin, as it's a general value for all
grids.
Each instance accesses this value through prepending the name of the class.
Similarly to prepending `this.` in front of instance accesses, here we prepend
`Grid.` in front of static accesses.
```ts twoslash
class Grid {
static origin = { x: 0, y: 0 };
```ts twoslash
// @errors: 2511 2339
abstract class Department {
constructor(public name: string) {}
printName(): void {
console.log("Department name: " + this.name);
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
console.log("Generating accounting reports...");
}
}
Constructor functions
When you declare a class in TypeScript, you are actually creating
multiple declarations at the same time. The first is the type of the
instance of the class.
We're also creating another value that we call the _constructor function_.
This is the function that is called when we `new` up instances of the class.
To see what this looks like in practice, let's take a look at the JavaScript
created by the above example:
```ts twoslash
// @strict: false
let Greeter = (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
})();
let greeter;
greeter = new Greeter("world");
console.log(greeter.greet()); // "Hello, world"
As we said in the previous section, a class declaration creates two things: a type
representing instances of the class and a constructor function.
Because classes create types, you can use them in the same places you would be able
to use interfaces.
```ts twoslash
// @strict: false
class Point {
x: number;
y: number;
}
```ts twoslash
enum Direction {
Up,
Down,
Left,
Right,
}
Here, Up would have the value 0 , Down would have 1 , etc. This
auto-incrementing behavior is useful for cases where we might not
care about the member values themselves, but do care that each value
is distinct from other values in the same enum.
```ts twoslash
// @errors: 1061
const getSomeValue = () => 23;
// ---cut---
enum E {
A = getSomeValue(),
B,
}
String enums
String enums are a similar concept, but have some subtle runtime
differences as documented below. In a string enum, each member has
to be constant-initialized with a string literal, or with another string
enum member.
While string enums don't have auto-incrementing behavior, string enums have the
benefit that they "serialize" well.
In other words, if you were debugging and had to read the runtime value of a
numeric enum, the value is often opaque - it doesn't convey any useful meaning on
its own (though [reverse mapping](#enums-at-runtime) can often help), string enums
allow you to give a meaningful and readable value when your code runs, independent
of the name of the enum member itself.
## Heterogeneous enums
Technically enums can be mixed with string and numeric members, but it's not clear
why you would ever want to do so:
```ts twoslash
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
enum E1 { X, Y, Z, }
enum E2 { A = 1, B, C, } ```
There is a special subset of constant enum members that aren't calculated: literal
enum members.
A literal enum member is a constant enum member with no initialized value, or with
values that are initialized to
When all members in an enum have literal enum values, some special semantics come
to play.
```ts twoslash
// @errors: 2322
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
kind: ShapeKind.Square,
radius: 100,
};
## Enums at runtime
```ts twoslash
enum E {
X,
Y,
Z,
}
Even though Enums are real objects that exist at runtime, the `keyof` keyword works
differently than you might expect for typical objects. Instead, use `keyof typeof`
to get a Type that represents all Enum keys as strings.
```ts twoslash
enum LogLevel {
ERROR,
WARN,
INFO,
DEBUG,
}
/**
* This is equivalent to:
* type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
*/
type LogLevelStrings = keyof typeof LogLevel;
Reverse mappings
In addition to creating an object with property names for members,
numeric enums members also get a reverse mapping from enum values
to enum names. For example, in this example:
```ts twoslash
// @showEmit
enum Enum {
A,
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
Keep in mind that string enum members do not get a reverse mapping
generated at all.
const enums
In most cases, enums are a perfectly valid solution. However sometimes
requirements are tighter. To avoid paying the cost of extra generated
code and additional indirection when accessing enum values, it's
possible to use const enums. Const enums are defined using the const
```ts twoslash
const enum Direction {
Up,
Down,
Left,
Right,
}
let directions = [
Direction.Up,
Direction.Down,
Direction.Left,
Direction.Right,
];
## Ambient enums
Ambient enums are used to describe the shape of already existing enum types.
```ts twoslash
declare enum Enum {
A = 1,
B,
C = 2,
}
One important difference between ambient and non-ambient enums is
that, in regular enums, members that don't have an initializer will be
considered constant if its preceding enum member is considered
constant. In contrast, an ambient (and non-const) enum member that
does not have initializer is always considered computed.
A major part of software engineering is building components that not
only have well-defined and consistent APIs, but are also reusable.
Components that are capable of working on the data of today as well
as the data of tomorrow will give you the most flexible capabilities for
building up large software systems.
In languages like C# and Java, one of the main tools in the toolbox
for creating reusable components is generics, that is, being able to
create a component that can work over a variety of types rather than
a single one. This allows users to consume these components and use
their own types.
Hello World of Generics
To start off, let's do the "hello world" of generics: the identity
function. The identity function is a function that will return back
whatever is passed in. You can think of this in a similar way to the
echo command.
Or, we could describe the identity function using the `any` type:
```ts twoslash
function identity(arg: any): any {
return arg;
}
While using any is certainly generic in that it will cause the function
to accept any and all types for the type of arg , we actually are
losing the information about what that type was when the function
returns. If we passed in a number, the only information we have is
that any type could be returned.
We say that this version of the `identity` function is generic, as it works over a
range of types.
Unlike using `any`, it's also just as precise (ie, it doesn't lose any information)
as the first `identity` function that used numbers for the argument and return
type.
Once we've written the generic identity function, we can call it in one of two
ways.
The first way is to pass all of the arguments, including the type argument, to the
function:
```ts twoslash
function identity<T>(arg: T): T {
return arg;
}
// ---cut---
let output = identity<string>("myString");
// ^?
The second way is also perhaps the most common. Here we use type
argument inference -- that is, we want the compiler to set the value
of T for us automatically based on the type of the argument we pass
in:
When you begin to use generics, you'll notice that when you create generic
functions like `identity`, the compiler will enforce that you use any generically
typed parameters in the body of the function correctly.
That is, that you actually treat these parameters as if they could be any and all
types.
```ts twoslash
function identity<T>(arg: T): T {
return arg;
}
What if we want to also log the length of the argument arg to the
console with each call? We might be tempted to write this:
Let's say that we've actually intended this function to work on arrays of `T`
rather than `T` directly. Since we're working with arrays, the `.length` member
should be available.
We can describe this just like we would create arrays of other types:
```ts twoslash
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
would bind to number . This allows us to use our generic type variable
T as part of the types we're working with, rather than the whole
type, giving us greater flexibility.
## Generic Types
The type of generic functions is just like those of non-generic functions, with the
type parameters listed first, similarly to function declarations:
```ts twoslash
function identity<T>(arg: T): T {
return arg;
}
We could also have used a different name for the generic type
parameter in the type, so long as the number of type variables and
how the type variables are used line up.
We can also write the generic type as a call signature of an object literal type:
```ts twoslash
function identity<T>(arg: T): T {
return arg;
}
Which leads us to writing our first generic interface. Let's take the
object literal from the previous example and move it to an interface:
```ts twoslash interface GenericIdentityFn { (arg: T): T; }
```ts twoslash
interface GenericIdentityFn<T> {
(arg: T): T;
}
This is a pretty literal use of the `GenericNumber` class, but you may have noticed
that nothing is restricting it to only use the `number` type.
We could have instead used `string` or even more complex objects.
```ts
// @strict: false
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
// ---cut---
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function (x, y) {
return x + y;
};
console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));
Just as with interface, putting the type parameter on the class itself
lets us make sure all of the properties of the class are working with
the same type.
As we covered in our section on classes, a class has two sides to its
type: the static side and the instance side. Generic classes are only
generic over their instance side rather than their static side, so when
working with classes, static members can not use the class's type
parameter.
Generic Constraints
If you remember from an earlier example, you may sometimes want to
write a generic function that works on a set of types where you have
some knowledge about what capabilities that set of types will have. In
our loggingIdentity example, we wanted to be able to access the
.length property of arg , but the compiler could not prove that every
type had a .length property, so it warns us that we can't make this
assumption.
Instead of working with any and all types, we'd like to constrain this function to
work with any and all types that also have the `.length` property.
As long as the type has this member, we'll allow it, but it's required to have at
least this member.
To do so, we must list our requirement as a constraint on what T can be.
```ts twoslash
interface Lengthwise {
length: number;
}
Instead, we need to pass in values whose type has all the required properties:
```ts twoslash
interface Lengthwise {
length: number;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
```ts twoslash
function create<T>(c: { new (): T }): T {
return new c();
}
createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask; ```
The handbook reference
The handbook reference is built to provide a richer understanding of
how a particular part of TypeScript works. You can read it top-to-
bottom, but each section aims to provide a deeper explanation of a
single concept - meaning there is no aim for continuity
This page lists some of the more advanced ways in which you can
model types, it works in tandem with the Utility Types doc which
includes types which are included in TypeScript and available globally.
Type Guards and
Differentiating Types
Union types are useful for modeling situations when values can overlap
in the types they can take on. What happens when we need to know
specifically whether we have a Fish ? A common idiom in JavaScript to
differentiate between two possible values is to check for the presence
of a member. As we mentioned, you can only access members that are
guaranteed to be in all the constituents of a union type.
```ts twoslash // @errors: 2339 type Fish = { swim: () => void }; type
Bird = { fly: () => void }; declare function getSmallPet(): Fish | Bird;
// ---cut--- let pet = getSmallPet();
To get the same code working via property accessors, we'll need to use a type
assertion:
```ts twoslash
type Fish = { swim: () => void };
type Bird = { fly: () => void };
declare function getSmallPet(): Fish | Bird;
// ---cut---
let pet = getSmallPet();
let fishPet = pet as Fish;
let birdPet = pet as Bird;
if (fishPet.swim) {
fishPet.swim();
} else if (birdPet.fly) {
birdPet.fly();
}
This isn't the sort of code you would want in your codebase however.
User-Defined Type Guards
It would be much better if once we performed the check, we could
know the type of pet within each branch.
```ts twoslash type Fish = { swim: () => void }; type Bird = { fly: () =>
void }; declare function getSmallPet(): Fish | Bird; // ---cut---
function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim
!== undefined; }
`pet is Fish` is our type predicate in this example.
A predicate takes the form `parameterName is Type`, where `parameterName` must be
the name of a parameter from the current function signature.
Any time `isFish` is called with some variable, TypeScript will _narrow_ that
variable to that specific type if the original type is compatible.
```ts twoslash
type Fish = { swim: () => void };
type Bird = { fly: () => void };
declare function getSmallPet(): Fish | Bird;
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
// ---cut---
// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet();
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
Notice that TypeScript not only knows that pet is a Fish in the if
branch; it also knows that in the else branch, you don't have a Fish ,
so you must have a Bird .
You may use the type guard isFish to filter an array of Fish | Bird
```ts twoslash // @errors: 2345 type Fish = { swim: () => void }; type
Bird = { fly: () => void }; declare function getSmallPet(): Fish | Bird;
function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim
!== undefined; } // ---cut--- const zoo: (Fish | Bird)[] =
[getSmallPet(), getSmallPet(), getSmallPet()]; const underWater1:
Fish[] = zoo.filter(isFish); // or, equivalently const underWater2: Fish[]
= zoo.filter(isFish); const underWater3: Fish[] = zoo.filter(pet =>
isFish(pet));
### Using the `in` operator
For a `n in x` expression, where `n` is a string literal or string literal type and
`x` is a union type, the "true" branch narrows to types which have an optional or
required property `n`, and the "false" branch narrows to types which have an
optional or missing property `n`.
```ts twoslash
type Fish = { swim: () => void };
type Bird = { fly: () => void };
// ---cut---
function move(pet: Fish | Bird) {
if ("swim" in pet) {
return pet.swim();
}
return pet.fly();
}
typeof type guards
Let's go back and write the code for a version of padLeft which uses
union types. We could write it with type predicates as follows:
```ts twoslash
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
1. the type of the function's `prototype` property if its type is not `any`
2. the union of types returned by that type's construct signatures
in that order.
## Nullable types
TypeScript has two special types, `null` and `undefined`, that have the values null
and undefined respectively.
We mentioned these briefly in [the Basic Types section](/docs/handbook/basic-
types.html).
```ts twoslash
// @errors: 2322
let exampleString = "foo";
exampleString = null;
stringOrNull = undefined;
From TypeScript 3.7 and onwards, you can use optional chaining to
simplify working with nullable types.
t l t d t
Optional parameters and properties
With --strictNullChecks , an optional parameter automatically adds |
undefined :
```ts twoslash
// @strict: false
// @strictNullChecks: true
// @errors: 2322
class C {
a: number;
b?: number;
}
c.a = 12;
c.a = undefined;
c.b = 13;
c.b = undefined;
c.b = null;
```ts twoslash
function f(stringOrNull: string | null): string {
return stringOrNull ?? "default";
}
if (user) { user.email.length; }
// Instead if you are sure that these objects or fields exist, the //
postfix ! lets you short circuit the nullability user!.email!.length;
## Type Aliases
```ts twoslash
type Second = number;
type Tree<T> = {
value: T;
left?: Tree<T>;
right?: Tree<T>;
};
As we mentioned, type aliases can act sort of like interfaces; however, there are
some subtle differences.
Almost all features of an `interface` are available in `type`, the key distinction
is that a type cannot be re-opened to add new properties vs an interface which is
always extendable.
<table class='full-width-table'>
<tbody>
<tr>
<th><code>Interface</code></th>
<th><code>Type</code></th>
</tr>
<tr>
<td>
<p>Extending an interface</p>
<code><pre>
interface Animal {
name: string
}<br/>
interface Bear extends Animal {
honey: boolean
}<br/>
const bear = getBear()
bear.name
bear.honey
</pre></code>
</td>
<td>
<p>Extending a type via intersections</p>
<code><pre>
type Animal = {
name: string
}<br/>
type Bear = Animal & {
honey: Boolean
}<br/>
const bear = getBear();
bear.name;
bear.honey;
</pre></code>
</td>
</tr>
<tr>
<td>
<p>Adding new fields to an existing interface</p>
<code><pre>
interface Window {
title: string
}<br/>
interface Window {
ts: import("typescript")
}<br/>
const src = 'const a = "Hello World"';
window.ts.transpileModule(src, {});
</pre></code>
</td>
<td>
<p>A type cannot be changed after being created</p>
<code><pre>
type Window = {
title: string
}<br/>
type Window = {
ts: import("typescript")
}<br/>
// Error: Duplicate identifier 'Window'.<br/>
</pre></code>
</td>
</tr>
</tbody>
</table>
Because an interface more closely maps how JavaScript objects work [by being open
to extension](https://wikipedia.org/wiki/Open/closed_principle), we recommend using
an interface over a type alias when possible.
On the other hand, if you can't express some shape with an interface and you need
to use a union or tuple type, type aliases are usually the way to go.
Much of the time when we talk about "singleton types", we're referring to both enum
member types as well as numeric/string literal types, though many users will use
"singleton types" and "literal types" interchangeably.
A polymorphic `this` type represents a type that is the _subtype_ of the containing
class or interface.
This is called _F_-bounded polymorphism, a lot of people know it as the [fluent
API](https://en.wikipedia.org/wiki/Fluent_interface) pattern.
This makes hierarchical fluent interfaces much easier to express, for example.
Take a simple calculator that returns `this` after each operation:
```ts twoslash
class BasicCalculator {
public constructor(protected value: number = 0) {}
public currentValue(): number {
return this.value;
}
public add(operand: number): this {
this.value += operand;
return this;
}
public multiply(operand: number): this {
this.value *= operand;
return this;
}
// ... other operations go here ...
}
Since the class uses this types, you can extend it and the new class
can use the old methods with no changes.
let v = new
ScientificCalculator(2).multiply(5).sin().add(1).currentValue();
Without `this` types, `ScientificCalculator` would not have been able to extend
`BasicCalculator` and keep the fluent interface.
`multiply` would have returned `BasicCalculator`, which doesn't have the `sin`
method.
However, with `this` types, `multiply` returns `this`, which is
`ScientificCalculator` here.
## Index types
With index types, you can get the compiler to check code that uses dynamic property
names.
For example, a common JavaScript pattern is to pick a subset of properties from an
object:
```js
function pluck(o, propertyNames) {
return propertyNames.map((n) => o[n]);
}
Here's how you would write and use this function in TypeScript, using
the index type query and indexed access operators:
```ts twoslash
interface Car {
manufacturer: string;
model: string;
year: number;
}
// ---cut---
let carProps: keyof Car;
// ^?
T[K] . Once you return the T[K] result, the compiler will instantiate the
actual type of the key, so the return type of getProperty will vary
according to which property you request.
`keyof` and `T[K]` interact with index signatures. An index signature parameter
type must be 'string' or 'number'.
If you have a type with a string index signature, `keyof T` will be `string |
number`
(and not just `string`, since in JavaScript you can access an object property
either
by using strings (`object["42"]`) or numbers (`object[42]`)).
And `T[string]` is just the type of the index signature:
```ts twoslash
interface Dictionary<T> {
[key: string]: T;
}
let keys: keyof Dictionary<number>;
// ^?
let value: Dictionary<number>["foo"];
// ^?
If you have a type with a number index signature, keyof T will just be
number .
## Mapped types
A common task is to take an existing type and make each of its properties optional:
```ts
interface PersonSubset {
name?: string;
age?: number;
}
interface PersonReadonly {
readonly name: string;
readonly age: number;
}
```ts twoslash
type Person = {
name: string;
age: number;
};
// ---cut---
type PersonPartial = Partial<Person>;
// ^?
type ReadonlyPerson = Readonly<Person>;
// ^?
Note that this syntax describes a type rather than a member. If you
want to add members, you can use an intersection type:
Let's take a look at the simplest mapped type and its parts:
```ts twoslash
type Keys = "option1" | "option2";
type Flags = { [K in Keys]: boolean };
The syntax resembles the syntax for index signatures with a for .. in
```ts twoslash
type Person = {
name: string;
age: number;
};
// ---cut---
type NullablePerson = { [P in keyof Person]: Person[P] | null };
// ^?
type PartialPerson = { [P in keyof Person]?: Person[P] };
// ^?
In these examples, the properties list is keyof T and the resulting type
is some variant of T[P] . This is a good template for any general use
of mapped types. That's because this kind of transformation is
homomorphic, which means that the mapping applies only to properties
of T and no others. The compiler knows that it can copy all the
existing property modifiers before adding any new ones. For example,
if Person.name was readonly, Partial<Person>.name would be readonly and
optional.
class:
Note that `Readonly<T>` and `Partial<T>` are so useful, they are included in
TypeScript's standard library along with `Pick` and `Record`:
```ts
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Note that `keyof any` represents the type of any value that can be used as an index
to an object. In otherwords, `keyof any` is currently equal to `string | number |
symbol`.
Now that you know how to wrap the properties of a type, the next thing you'll want
to do is unwrap them.
Fortunately, that's pretty easy:
```ts twoslash
type Proxy<T> = {
get(): T;
set(value: T): void;
};
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>;
};
T extends U ? X : Y
```ts twoslash
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
type T0 = TypeName<string>;
// ^?
type T1 = TypeName<"a">;
// ^?
type T2 = TypeName<true>;
// ^?
type T3 = TypeName<() => void>;
// ^?
type T4 = TypeName<string[]>;
// ^?
In the meantime, we can assign a conditional type to any other target type as long
as each branch of the conditional is assignable to that target.
So in our example above we were able to assign `U extends Foo ? string : number` to
`string | number` since no matter what the conditional evaluates to, it's known to
be either `string` or `number`.
Conditional types in which the checked type is a naked type parameter are called
_distributive conditional types_.
Distributive conditional types are automatically distributed over union types
during instantiation.
For example, an instantiation of `T extends U ? X : Y` with the type argument `A |
B | C` for `T` is resolved as `(A extends U ? X : Y) | (B extends U ? X : Y) | (C
extends U ? X : Y)`.
#### Example
```ts twoslash
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
// ---cut---
type T5 = TypeName<string | (() => void)>;
// ^?
type T6 = TypeName<string | string[] | undefined>;
// ^?
type T7 = TypeName<string[] | number[]>;
// ^?
In instantiations of a distributive conditional type T extends U ? X : Y ,
references to T within the conditional type are resolved to individual
constituents of the union type (i.e. T refers to the individual
constituents after the conditional type is distributed over the union
type). Furthermore, references to T within X have an additional
type parameter constraint U (i.e. T is considered assignable to U
within X ).
Example
```ts twoslash type BoxedValue = { value: T }; type BoxedArray = {
array: T[] }; type Boxed = T extends any[] ? BoxedArray :
BoxedValue;
```ts twoslash
// @errors: 2300 2322
// Remove types from T that are assignable to U
type Diff<T, U> = T extends U ? never : T;
// Remove types from T that are not assignable to U
type Filter<T, U> = T extends U ? T : never;
Similar to union and intersection types, conditional types are not permitted to
reference themselves recursively.
For example the following is an error.
#### Example
```ts twoslash
// @errors: 2456 2315
type ElementType<T> = T extends any[] ? ElementType<T[number]> : T; // Error
Type inference in conditional
types
Within the extends clause of a conditional type, it is now possible to
have infer declarations that introduce a type variable to be inferred.
Such inferred type variables may be referenced in the true branch of
the conditional type. It is possible to have multiple infer locations for
the same type variable.
```ts twoslash
type Unpacked<T> = T extends (infer U)[]
? U
: T extends (...args: any[]) => infer U
? U
: T extends Promise<infer U>
? U
: T;
type T0 = Unpacked<string>;
// ^?
type T1 = Unpacked<string[]>;
// ^?
type T2 = Unpacked<() => string>;
// ^?
type T3 = Unpacked<Promise<string>>;
// ^?
type T4 = Unpacked<Promise<string>[]>;
// ^?
type T5 = Unpacked<Unpacked<Promise<string>[]>>;
// ^?
```ts twoslash
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type T1 = Bar<{ a: (x: string) => void; b: (x: string) => void }>;
// ^?
type T2 = Bar<{ a: (x: string) => void; b: (x: number) => void }>;
// ^?
When inferring from a type with multiple call signatures (such as the
type of an overloaded function), inferences are made from the last
signature (which, presumably, is the most permissive catch-all case). It
is not possible to perform overload resolution based on a list of
argument types.
type T1 = ReturnType; // ^?
```ts twoslash
// @errors: 1338 2304
type ReturnedType<T extends (...args: any[]) => infer R> = R;
However, much the same effect can be obtained by erasing the type
variables in the constraint and instead specifying a conditional type:
ts twoslash // @noErrors type AnyFunction = (...args: any[]) => any; type ReturnType<T
extends AnyFunction> = T extends (...args: any[]) => infer R ? R : any;
Predefined conditional types
TypeScript adds several predefined conditional types, you can find the
full list and examples in Utility Types.
TypeScript provides several utility types to facilitate common type
transformations. These utilities are available globally.
Partial<Type>
Constructs a type with all properties of Type set to optional. This
utility will return a type that represents all subsets of a given type.
Example
## `Readonly<Type>`
Constructs a type with all properties of `Type` set to `readonly`, meaning the
properties of the constructed type cannot be reassigned.
##### Example
```ts twoslash
// @errors: 2540
interface Todo {
title: string;
}
todo.title = "Hello";
Object.freeze
function freeze<Type>(obj: Type): Readonly<Type>;
Record<Keys,Type>
Constructs a type with a set of properties Keys of type Type . This
utility can be used to map the properties of a type to another type.
Example
nav.about; // ^?
## `Pick<Type, Keys>`
##### Example
```ts twoslash
interface Todo {
title: string;
description: string;
completed: boolean;
}
todo;
// ^?
Omit<Type, Keys>
Constructs a type by picking all properties from Type and then
removing Keys .
Example
todo; // ^?
## `Exclude<Type, ExcludedUnion>`
Constructs a type by excluding from `Type` all union members that are assignable to
`ExcludedUnion`.
##### Example
```ts twoslash
type T0 = Exclude<"a" | "b" | "c", "a">;
// ^?
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// ^?
type T2 = Exclude<string | number | (() => void), Function>;
// ^?
Extract<Type, Union>
Constructs a type by extracting from Type all union members that are
assignable to Union .
Example
## `NonNullable<Type>`
##### Example
```ts twoslash
type T0 = NonNullable<string | number | undefined>;
// ^?
type T1 = NonNullable<string[] | null | undefined>;
// ^?
Parameters<Type>
Constructs a tuple type from the types used in the parameters of a
function type Type .
Example
## `ConstructorParameters<Type>`
Constructs a tuple or array type from the types of a constructor function type. It
produces a tuple type with all the parameter types (or the type `never` if `Type`
is not a function).
##### Example
```ts twoslash
// @errors: 2344
// @strict: false
type T0 = ConstructorParameters<ErrorConstructor>;
// ^?
type T1 = ConstructorParameters<FunctionConstructor>;
// ^?
type T2 = ConstructorParameters<RegExpConstructor>;
// ^?
type T3 = ConstructorParameters<any>;
// ^?
type T4 = ConstructorParameters<Function>;
// ^?
ReturnType<Type>
Constructs a type consisting of the return type of function Type .
Example
## `InstanceType<Type>`
##### Example
```ts twoslash
// @errors: 2344 2344
// @strict: false
class C {
x = 0;
y = 0;
}
Example
## `ThisParameterType<Type>`
##### Example
```ts twoslash
function toHex(this: Number) {
return this.toString(16);
}
Example
console.log(fiveToHex());
## `ThisType<Type>`
This utility does not return a transformed type. Instead, it serves as a marker for
a contextual [`this`](/docs/handbook/functions.html#this) type. Note that the `--
noImplicitThis` flag must be enabled to use this utility.
##### Example
```ts twoslash
// @noImplicitThis: false
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};
obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
x: number, y: number } & { moveBy(dx: number, dy: number): number } . Notice how
the type of the methods property simultaneously is an inference target
and a source for the this type in methods.
The ThisType<T> marker interface is simply an empty interface declared
in lib.d.ts . Beyond being recognized in the contextual type of an
object literal, the interface acts like any empty interface.
Introduction
With the introduction of Classes in TypeScript and ES6, there now exist
certain scenarios that require additional features to support annotating
or modifying classes and class members. Decorators provide a way to
add both annotations and a meta-programming syntax for class
declarations and members. Decorators are a stage 2 proposal for
JavaScript and are available as an experimental feature of TypeScript.
Command Line:
tsconfig.json:
For example, given the decorator `@sealed` we might write the `sealed` function as
follows:
```ts
function sealed(target) {
// do something with 'target' ...
}
On a single line:
@f @g x
On multiple lines:
@f
@g
x
function g() {
console.log("g(): evaluated");
return function (
target,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log("g(): called");
};
}
class C {
@f()
@g()
method() {}
}
f(): evaluated
g(): evaluated
g(): called
f(): called
Decorator Evaluation
There is a well defined order to how decorators applied to various
declarations inside of a class are applied:
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
When @sealed is executed, it will seal both the constructor and its
prototype.
@classDecorator
class Greeter {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
}
console.log(new Greeter("world"));
Method Decorators
A Method Decorator is declared just before a method declaration. The
decorator is applied to the Property Descriptor for the method, and
can be used to observe, modify, or replace a method definition. A
method decorator cannot be used in a declaration file, on an overload,
or in any other ambient context (such as in a declare class).
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
@configurable(false)
get x() {
return this._x;
}
@configurable(false)
get y() {
return this._y;
}
}
class Greeter {
@format("Hello, %s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
let formatString = getFormat(this, "greeting");
return formatString.replace("%s", this.greeting);
}
}
We can then define the @format decorator and getFormat functions
using the following function declarations:
import "reflect-metadata";
library. When getFormat is called, it reads the metadata value for the
format.
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@validate
greet(@required name: string) {
return "Hello " + name + ", " + this.greeting;
}
}
We can then define the @required and @validate decorators using the
following function declarations:
import "reflect-metadata";
function required(
target: Object,
propertyKey: string | symbol,
parameterIndex: number
) {
let existingRequiredParameters: number[] =
Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata(
requiredMetadataKey,
existingRequiredParameters,
target,
propertyKey
);
}
function validate(
target: any,
propertyName: string,
descriptor: TypedPropertyDescriptor<Function>
) {
let method = descriptor.value;
descriptor.value = function () {
let requiredParameters: number[] = Reflect.getOwnMetadata(
requiredMetadataKey,
target,
propertyName
);
if (requiredParameters) {
for (let parameterIndex of requiredParameters) {
if (
parameterIndex >= arguments.length ||
arguments[parameterIndex] === undefined
) {
throw new Error("Missing required argument.");
}
}
}
Command Line:
tsconfig.json:
```ts
import "reflect-metadata";
class Point {
x: number;
y: number;
}
class Line {
private _p0: Point;
private _p1: Point;
@validate
set p0(value: Point) {
this._p0 = value;
}
get p0() {
return this._p0;
}
@validate
set p1(value: Point) {
this._p1 = value;
}
get p1() {
return this._p1;
}
}
function validate<T>(
target: any,
propertyKey: string,
descriptor: TypedPropertyDescriptor<T>
) {
let set = descriptor.set;
descriptor.set = function (value: T) {
let type = Reflect.getMetadata("design:type", target, propertyKey);
if (!(value instanceof type)) {
throw new TypeError("Invalid type.");
}
set.call(target, value);
};
}
class Line {
private _p0: Point;
private _p1: Point;
@validate
@Reflect.metadata("design:type", Point)
set p0(value: Point) {
this._p0 = value;
}
get p0() {
return this._p0;
}
@validate
@Reflect.metadata("design:type", Point)
set p1(value: Point) {
this._p1 = value;
}
get p1() {
return this._p1;
}
}
For the purposes of this article, "declaration merging" means that the
compiler merges two separate declarations declared with the same name
into a single definition. This merged definition has the features of
both of the original declarations. Any number of declarations can be
merged; it's not limited to just two declarations.
Basic Concepts
In TypeScript, a declaration creates entities in at least one of three
groups: namespace, type, or value. Namespace-creating declarations
create a namespace, which contains names that are accessed using a
dotted notation. Type-creating declarations do just that: they create
a type that is visible with the declared shape and bound to the given
name. Lastly, value-creating declarations create values that are visible
in the output JavaScript.
Namespace X X
Class X X
Enum X X
Interface X
Type Alias X
Function X
Variable X
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
Notice that the elements of each group maintains the same order, but
the groups themselves are merged with later overload sets ordered
first.
interface Document {
createElement(tagName: "canvas"): HTMLCanvasElement;
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
createElement(tagName: string): HTMLElement;
createElement(tagName: any): Element;
}
Merging Namespaces
Similarly to interfaces, namespaces of the same name will also merge
their members. Since namespaces create both a namespace and a value,
we need to understand how both merge.
namespace Animals {
export class Zebra {}
}
namespace Animals {
export interface Legged {
numberOfLegs: number;
}
export class Dog {}
}
is equivalent to:
namespace Animals {
export interface Legged {
numberOfLegs: number;
}
namespace Animal {
let haveMuscles = true;
namespace Animal {
export function doAnimalsHaveMuscles() {
return haveMuscles; // Error, because haveMuscles is not accessible here
}
}
function that shares the same un-merged namespace can see the
symbol. The doAnimalsHaveMuscles function, even though it's part of the
merged Animal namespace can not see this un-exported member.
Merging Namespaces with
Classes, Functions, and Enums
Namespaces are flexible enough to also merge with other types of
declarations. To do so, the namespace declaration must follow the
declaration it will merge with. The resulting declaration has properties
of both declaration types. TypeScript uses this capability to model
some of the patterns in JavaScript as well as other programming
languages.
Merging Namespaces with
Classes
This gives the user a way of describing inner classes.
class Album {
label: Album.AlbumLabel;
}
namespace Album {
export class AlbumLabel {}
}
The visibility rules for merged members is the same as described in the
Merging Namespaces section, so we must export the AlbumLabel class
for the merged class to see it. The end result is a class managed
inside of another class. You can also use namespaces to add more
static members to an existing class.
namespace buildLabel {
export let suffix = "";
export let prefix = "Hello, ";
}
console.log(buildLabel("Sam Smith"));
namespace Color {
export function mixColor(colorName: string) {
if (colorName == "yellow") {
return Color.red + Color.green;
} else if (colorName == "white") {
return Color.red + Color.green + Color.blue;
} else if (colorName == "magenta") {
return Color.red + Color.blue;
} else if (colorName == "cyan") {
return Color.green + Color.blue;
}
}
}
Disallowed Merges
Not all merges are allowed in TypeScript. Currently, classes can not
merge with other classes or with variables. For information on mimicking
class merging, see the Mixins in TypeScript section.
Module Augmentation
Although JavaScript modules do not support merging, you can patch
existing objects by importing and then updating them. Let's look at a
toy Observable example:
// observable.ts
export class Observable<T> {
// ... implementation left as an exercise for the reader ...
}
// map.ts
import { Observable } from "./observable";
Observable.prototype.map = function (f) {
// ... another exercise for the reader
};
This works fine in TypeScript too, but the compiler doesn't know about
Observable.prototype.map . You can use module augmentation to tell the
compiler about it:
// observable.ts
export class Observable<T> {
// ... implementation left as an exercise for the reader ...
}
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
interface Observable<T> {
map<U>(f: (x: T) => U): Observable<U>;
}
}
Observable.prototype.map = function (f) {
// ... another exercise for the reader
};
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());
// observable.ts
export class Observable<T> {
// ... still no implementation ...
}
declare global {
interface Array<T> {
toObservable(): Observable<T>;
}
}
Array.prototype.toObservable = function () {
// ...
};
Code generation
The compiler will generate a simple for loop for a for..of loop, for
instance:
t t d
Targeting ECMAScript 2015 and
higher
When targeting an ECMAScipt 2015-compliant engine, the compiler will
generate for..of loops to target the built-in iterator implementation
in the engine.
JSX is an embeddable XML-like syntax. It is meant to be transformed
into valid JavaScript, though the semantics of that transformation are
implementation-specific. JSX rose to popularity with the React
framework, but has since seen other implementations as well. TypeScript
supports embedding, type checking, and compiling JSX directly to
JavaScript.
Basic usage
In order to use JSX you must do two things.
TypeScript ships with three JSX modes: preserve , react , and react-
native . These modes only affect the emit stage - type checking is
unaffected. The preserve mode will keep the JSX as part of the
output to be further consumed by another transform step (e.g.
Babel). Additionally the output will have a .jsx file extension. The
react mode will emit React.createElement , does not need to go through
a JSX transformation before use, and the output will have a .js file
extension. The react-native mode is the equivalent of preserve in that
it keeps all JSX, but the output will instead have a .js file extension.
Output File
Mode Input Output
Extension
<div
preserve <div /> .jsx
/>
<div
react React.createElement("div") .js
/>
react- <div
<div /> .js
native />
react- <div
_jsx("div", {}, void 0); .js
jsx />
You can specify this mode using either the --jsx command line flag or
the corresponding option jsx in your tsconfig.json file.
*Note: You can specify the JSX factory function to use when
targeting react JSX emit with --jsxFactory option (defaults to
React.createElement )
The as operator
Recall how to write a type assertion:
This asserts the variable bar to have the type foo . Since TypeScript
also uses angle brackets for type assertions, combining it with JSX's
syntax would introduce certain parsing difficulties. As a result,
TypeScript disallows angle bracket type assertions in .tsx files.
Since the above syntax cannot be used in .tsx files, an alternate type
assertion operator should be used: as . The example can easily be
rewritten with the as operator.
TypeScript uses the same convention that React does for distinguishing
between these. An intrinsic element always begins with a lowercase
letter, and a value-based element always begins with an uppercase
letter.
Intrinsic elements
Intrinsic elements are looked up on the special interface
JSX.IntrinsicElements . By default, if this interface is not specified, then
anything goes and intrinsic elements will not be type checked. However,
if this interface is present, then the name of the intrinsic element is
looked up as a property on the JSX.IntrinsicElements interface. For
example:
<foo />; // ok
<bar />; // error
In the above example, <foo /> will work fine but <bar /> will result in
an error since it has not been specified on JSX.IntrinsicElements .
<MyComponent />; // ok
<SomeOtherComponent />; // error
Function Component
As the name suggests, the component is defined as a JavaScript
function where its first argument is a props object. TS enforces that
its return type must be assignable to JSX.Element .
interface FooProp {
name: string;
X: number;
Y: number;
}
interface ClickableProps {
children: JSX.Element[] | JSX.Element
}
Class Component
It is possible to define the type of a class component. However, to
do so it is best to understand two new terms: the element class type
and the element instance type.
Given <Expr /> , the element class type is the type of Expr . So in the
example above, if MyComponent was an ES6 class the class type would be
that class's constructor and statics. If MyComponent was a factory
function, the class type would be that function.
class MyComponent {
render() {}
}
function MyFactoryFunction() {
return {
render: () => {},
};
}
class MyComponent {
render() {}
}
function MyFactoryFunction() {
return { render: () => {} };
}
<MyComponent />; // ok
<MyFactoryFunction />; // ok
class NotAValidComponent {}
function NotAValidFactoryFunction() {
return {};
}
class MyComponent {
// specify the property on the element instance type
props: {
foo?: string;
};
}
The element attribute type is used to type check the attributes in the
JSX. Optional and required properties are supported.
type may also be used to specify the same kind of extra attributes
just for class components (and not Function Components). In this
type, the generic parameter corresponds to the class instance type. In
React, this is used to allow the ref attribute of type Ref<T> .
Generally speaking, all of the properties on these interfaces should be
optional, unless you intend that users of your JSX framework need to
provide some attribute on every tag.
<div>
<h1>Hello</h1>
</div>;
<div>
<h1>Hello</h1>
World
</div>;
You can specify the type of children like any other attribute. This will
override the default type from, eg the React typings if you use them.
interface PropsType {
children: JSX.Element
name: string
}
// OK
<Component name="foo">
<h1>Hello World</h1>
</Component>
var a = (
<div>
{["foo", "bar"].map((i) => (
<span>{i / 2}</span>
))}
</div>
);
The above code will result in an error since you cannot divide a string
by a number. The output, when using the preserve option, looks like:
var a = (
<div>
{["foo", "bar"].map(function (i) {
return <span>{i / 2}</span>;
})}
</div>
);
React integration
To use JSX with React you should use the React typings. These typings
define the JSX namespace appropriately for use with React.
interface Props {
foo: string;
}
Configuring JSX
There are multiple compiler flags which can be used to customize your
JSX, which work as both a compiler flag and via inline per-file
pragmas. To learn more see their tsconfig reference pages:
jsxFactory
jsxFragmentFactory
jsxImportSource
Along with traditional OO hierarchies, another popular way of building
up classes from reusable components is to build them by combining
simpler partial classes. You may be familiar with the idea of mixins or
traits for languages like Scala, and the pattern has also reached some
popularity in the JavaScript community.
How Does A Mixin Work?
The pattern relies on using Generics with class inheritance to extend a
base class. TypeScript's best mixin support is done via the class
expression pattern. You can read more about how this pattern works in
JavaScript here.
To get started, we'll need a class which will have the mixin's applied
on top of:
```ts twoslash
// To get started, we need a type which we'll use to extend
// other classes from. The main responsibility is to declare
// that the type being passed in is a class.
setScale(scale: number) {
this._scale = scale;
}
With these all set up, then you can create a class which represents the
base class with mixins applied:
## Constrained Mixins
In the above form, the mixin's have no underlying knowledge of the class which can
make it hard to create the design you want.
```ts twoslash
// This was our previous constructor:
type Constructor = new (...args: any[]) => {};
// Now we use a generic version which can apply a constraint on
// the class which this mixin is applied to
type GConstructor<T = {}> = new (...args: any[]) => T;
This allows for creating classes which only work with constrained base
classes:
```ts twoslash type GConstructor = new (…args: any[]) => T; class Sprite
{ name = ""; x = 0; y = 0;
```ts twoslash
type GConstructor<T = {}> = new (...args: any[]) => T;
class Sprite {
name = "";
x = 0;
y = 0;
constructor(name: string) {
this.name = name;
}
}
type Positionable = GConstructor<{ setPos: (x: number, y: number) => void }>;
type Spritable = GConstructor<typeof Sprite>;
type Loggable = GConstructor<{ print: () => void }>;
// ---cut---
## Constraints
The mixin pattern is supported natively inside the TypeScript compiler by code flow
analysis.
There are a few cases where you can hit the edges of the native support.
You cannot use decorators to provide mixins via code flow analysis:
```ts twoslash
// @experimentalDecorators
// @errors: 2339
// A decorator function which replicates the mixin pattern:
const Pausable = (target: typeof Player) => {
return class Pausable extends target {
shouldFreeze = false;
};
};
@Pausable
class Player {
x = 0;
y = 0;
}
// The Player class does not have the decorator's type merged:
const player = new Player();
player.shouldFreeze;
You can work around this by using functions to return your classes
which differ based on a generic:
Modules are executed within their own scope, not in the global scope;
this means that variables, functions, classes, etc. declared in a module
are not visible outside the module unless they are explicitly exported
using one of the export forms. Conversely, to consume a variable,
function, class, interface, etc. exported from a different module, it
has to be imported using one of the import forms.
Exporting a declaration
Any declaration (such as a variable, function, class, type alias, or
interface) can be exported by adding the export keyword.
StringValidator.ts
ZipCodeValidator.ts
ParseIntBasedZipCodeValidator.ts
Optionally, a module can wrap one or more modules and combine all
their exports using export * from "module" syntax.
AllValidators.ts
forms below:
Import a single export from
a module
import { ZipCodeValidator } from "./ZipCodeValidator";
import "./my-module.js";
Importing Types
Prior to TypeScript 3.8, you can import a type using import . With
TypeScript 3.8, you can import a type using the import statement, or
using import type .
default exports are really handy. For instance, a library like jQuery
might have a default export of jQuery or $ , which we'd probably
also import under the name $ or jQuery .
JQuery.d.ts
App.ts
$("button.continue").html("Next Step...");
ZipCodeValidator.ts
Test.ts
import validator from "./ZipCodeValidator";
or
StaticZipCodeValidator.ts
Test.ts
OneTwoThree.ts
Log.ts
console.log(num); // "123"
Export all as x
With TypeScript 3.8, you can use export * as ns as a shorthand for re-
exporting another module with a name:
They also support replacing the exports object with a custom single
object. Default exports are meant to act as a replacement for this
behavior; however, the two are incompatible. TypeScript supports export
The export = syntax specifies a single object that is exported from the
module. This can be a class, interface, namespace, function, or enum.
ZipCodeValidator.ts
Test.ts
import zip = require("./ZipCodeValidator");
// Validators to use
let validator = new zip();
This simple example shows how the names used during importing and
exporting get translated into the module loading code.
SimpleModule.ts
import m = require("mod");
export let t = m.something + 1;
UMD SimpleModule.js
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
} else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./mod"], factory);
}
})(function (require, exports) {
var mod_1 = require("./mod");
exports.t = mod_1.something + 1;
});
System SimpleModule.js
When compiled, each module will become a separate .js file. As with
reference tags, the compiler will follow import statements to compile
dependent files.
Validation.ts
LettersOnlyValidator.ts
ZipCodeValidator.ts
import { StringValidator } from "./Validation";
Test.ts
// Validators to use
let validators: { [s: string]: StringValidator } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();
To maintain type safety, we can use the typeof keyword. The typeof
if (needZipValidation) {
let ZipCodeValidator: typeof Zip = require("./ZipCodeValidator");
let validator = new ZipCodeValidator();
if (validator.isAcceptable("...")) {
/* ... */
}
}
if (needZipValidation) {
require(["./ZipCodeValidator"], (ZipCodeValidator: typeof Zip) => {
let validator = new ZipCodeValidator.ZipCodeValidator();
if (validator.isAcceptable("...")) {
/* ... */
}
});
}
if (needZipValidation) {
System.import("./ZipCodeValidator").then((ZipCodeValidator: typeof Zip) => {
var x = new ZipCodeValidator();
if (x.isAcceptable("...")) {
/* ... */
}
});
}
Working with Other JavaScript
Libraries
To describe the shape of libraries not written in TypeScript, we need
to declare the API that the library exposes.
Now we can /// <reference> node.d.ts and then load the modules using
import url = require("url"); or import * as URL from "url" .
t d t d l
Shorthand ambient modules
If you don't want to take the time to write out declarations before
using a new module, you can use a shorthand declaration to get
started quickly.
declarations.d.ts
All imports from a shorthand module will have the any type.
d l
UMD modules
Some libraries are designed to be used in many module loaders, or
with no module loading (global variables). These are known as UMD
modules. These libraries can be accessed through either an import or a
global variable. For example:
math-lib.d.ts
mathLib.isPrime(2);
Guidance for structuring
modules
l t
MyClass.ts
export default class SomeType {
constructor() { ... }
}
MyFunc.ts
export default function getThing() {
return "thing";
}
Consumer.ts
import t from "./MyClass";
import f from "./MyFunc";
let x = new t();
console.log(f());
This is optimal for consumers. They can name your type whatever they
want ( t in this case) and don't have to do any excessive dotting to
find your objects.
MyThings.ts
export class SomeType {
/* ... */
}
export function someFunc() {
/* ... */
}
Consumer.ts
import { SomeType, someFunc } from "./MyThings";
let x = new SomeType();
let y = someFunc();
MyLargeModule.ts
export class Dog { ... }
export class Cat { ... }
export class Tree { ... }
export class Flower { ... }
Consumer.ts
import * as myLargeModule from "./MyLargeModule.ts";
let x = new myLargeModule.Dog();
Re-export to extend
Often you will need to extend functionality on a module. A common JS
pattern is to augment the original object with extensions, similar to
how JQuery extensions work. As we've mentioned before, modules do
not merge like global namespace objects would. The recommended
solution is to not mutate the original object, but rather export a new
entity that provides the new functionality.
Calculator.ts
export class Calculator {
private current = 0;
private memory = 0;
private operator: string;
protected evaluateOperator(
operator: string,
left: number,
right: number
): number {
switch (this.operator) {
case "+":
return left + right;
case "-":
return left - right;
case "*":
return left * right;
case "/":
return left / right;
}
}
private evaluate() {
if (this.operator) {
this.memory = this.evaluateOperator(
this.operator,
this.memory,
this.current
);
} else {
this.memory = this.current;
}
this.current = 0;
}
public handleChar(char: string) {
if (char === "=") {
this.evaluate();
return;
} else {
let value = this.processDigit(char, this.current);
if (value !== undefined) {
this.current = value;
return;
} else {
let value = this.processOperator(char);
if (value !== undefined) {
this.evaluate();
this.operator = value;
return;
}
}
}
throw new Error(`Unsupported input: '${char}'`);
}
public getResult() {
return this.memory;
}
}
Here is a simple test for the calculator using the exposed test
function.
TestCalculator.ts
import { Calculator, test } from "./Calculator";
ProgrammerCalculator.ts
import { Calculator } from "./Calculator";
TestProgrammerCalculator.ts
import { Calculator, test } from "./ProgrammerCalculator";
Module resolution is the process the compiler uses to figure out what
an import refers to. Consider an import statement like import { a } from
At this point, the compiler will ask "what's the shape of moduleA ?"
While this sounds straightforward, moduleA could be defined in one of
your own .ts / .tsx files, or in a .d.ts that your code depends on.
First, the compiler will try to locate a file that represents the
imported module. To do so the compiler follows one of two different
strategies: Classic or Node. These strategies tell the compiler where to
look for moduleA .
Finally, if the compiler could not resolve the module, it will log an
error. In this case, the error would be something like error TS2307:
Cannot find module 'moduleA'.
Relative vs. Non-relative
module imports
Module imports are resolved differently based on whether the module
reference is relative or non-relative.
import "/mod";
Classic
This used to be TypeScript's default resolution strategy. Nowadays,
this strategy is mainly present for backward compatibility.
1. /root/src/folder/moduleB.ts
2. /root/src/folder/moduleB.d.ts
For example:
2. /root/src/folder/moduleB.d.ts
3. /root/src/moduleB.ts
4. /root/src/moduleB.d.ts
5. /root/moduleB.ts
6. /root/moduleB.d.ts
7. /moduleB.ts
8. /moduleB.d.ts
Node
This resolution strategy attempts to mimic the Node.js module
resolution mechanism at runtime. The full Node.js resolution algorithm
is outlined in Node.js module documentation.
1. /root/src/node_modules/moduleB.js
property)
3. /root/src/node_modules/moduleB/index.js
4. /root/node_modules/moduleB.js
property)
6. /root/node_modules/moduleB/index.js
7. /node_modules/moduleB.js
t l d l
How TypeScript resolves modules
TypeScript will mimic the Node.js run-time resolution strategy in order
to locate definition files for modules at compile-time. To accomplish
this, TypeScript overlays the TypeScript source file extensions ( .ts ,
.tsx , and .d.ts ) over Node's resolution logic. TypeScript will also
use a field in package.json named "types" to mirror the purpose of
"main" - the compiler will use it to find the "main" definition file to
consult.
1. /root/src/moduleB.ts
2. /root/src/moduleB.tsx
3. /root/src/moduleB.d.ts
6. /root/src/moduleB/index.tsx
7. /root/src/moduleB/index.d.ts
1. /root/src/node_modules/moduleB.ts
2. /root/src/node_modules/moduleB.tsx
3. /root/src/node_modules/moduleB.d.ts
property)
5. /root/src/node_modules/@types/moduleB.d.ts
6. /root/src/node_modules/moduleB/index.ts
7. /root/src/node_modules/moduleB/index.tsx
8. /root/src/node_modules/moduleB/index.d.ts
9. /root/node_modules/moduleB.ts
10. /root/node_modules/moduleB.tsx
11. /root/node_modules/moduleB.d.ts
property)
13. /root/node_modules/@types/moduleB.d.ts
14. /root/node_modules/moduleB/index.ts
15. /root/node_modules/moduleB/index.tsx
16. /root/node_modules/moduleB/index.d.ts
17. /node_modules/moduleB.ts
18. /node_modules/moduleB.tsx
19. /node_modules/moduleB.d.ts
22. /node_modules/moduleB/index.ts
23. /node_modules/moduleB/index.tsx
24. /node_modules/moduleB/index.d.ts
Base URL
Using a baseUrl is a common practice in applications using AMD module
loaders where modules are "deployed" to a single folder at run-time.
The sources of these modules can live in different directories, but a
build script will put them all together.
Setting baseUrl informs the compiler where to find modules. All module
imports with non-relative names are assumed to be relative to the
baseUrl .
Note that relative module imports are not impacted by setting the
baseUrl, as they are always resolved relative to their importing files.
Path mapping
Sometimes modules are not directly located under baseUrl. For
instance, an import to a module "jquery" would be translated at
runtime to "node_modules/jquery/dist/jquery.slim.min.js" . Loaders use a
mapping configuration to map module names to files at run-time, see
RequireJs documentation and SystemJS documentation.
Using `"paths"` 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.
A build step would put them all together in one place.
The project layout may look like:
```tree
projectRoot
├── folder1
│ ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│ └── file2.ts
├── generated
│ ├── folder1
│ └── folder2
│ └── file3.ts
└── tsconfig.json
Following this logic, the compiler will attempt to resolve the two imports as such:
import 'folder1/file2':
1. pattern '\*' is matched and wildcard captures the whole module name
2. try first substitution in the list: '\*' -> `folder1/file2`
3. result of substitution is non-relative name - combine it with _baseUrl_ ->
`projectRoot/folder1/file2.ts`.
4. File exists. Done.
import 'folder2/file3':
1. pattern '\*' is matched and wildcard captures the whole module name
2. try first substitution in the list: '\*' -> `folder2/file3`
3. result of substitution is non-relative name - combine it with _baseUrl_ ->
`projectRoot/folder2/file3.ts`.
4. File does not exist, move to the second substitution
5. second substitution 'generated/\*' -> `generated/folder2/file3`
6. result of substitution is non-relative name - combine it with _baseUrl_ ->
`projectRoot/generated/folder2/file3.ts`.
7. File exists. Done.
Sometimes the project sources from multiple directories at compile time are all
combined to generate a single output directory.
This can be viewed as a set of source directories create a "virtual" directory.
Using 'rootDirs', you can inform the compiler of the _roots_ making up this
"virtual" directory;
and thus the compiler can resolve relative modules imports within these "virtual"
directories _as if_ were merged together in one directory.
```tree
src
└── views
└── view1.ts (imports './template1')
└── view2.ts
generated
└── templates
└── views
└── template1.ts (imports './view2')
Assume that each of these modules exports an array of strings. For example
`./zh/messages` might contain:
```ts
export default [" 您好吗", "很⾼兴认识你"];
As discussed earlier, the compiler can visit files outside the current folder when
resolving a module.
This can be hard when diagnosing why a module is not resolved, or is resolved to an
incorrect definition.
Enabling the compiler module resolution tracing using `--traceResolution` provides
insight in what happened during the module resolution process.
Let's say we have a sample application that uses the `typescript` module.
`app.ts` has an import like `import * as ts from "typescript"`.
```tree
│ tsconfig.json
├───node_modules
│ └───typescript
│ └───lib
│ typescript.d.ts
└───src
app.ts
tsc --traceResolution
Final result
For instance:
app.ts
import * as A from "moduleA"; // OK, 'moduleA' passed on the command-line
import * as B from "moduleB"; // Error TS2307: Cannot find module 'moduleB'.
This post outlines the various ways to organize your code using
namespaces (previously "internal modules") in TypeScript. As we alluded
in our note about terminology, "internal modules" are now referred to
as "namespaces". Additionally, anywhere the module keyword was used
when declaring an internal module, the namespace keyword can and
should be used instead. This avoids confusing new users by overloading
them with similarly named terms.
First steps
Let's start with the program we'll be using as our example throughout
this page. We've written a small set of simplistic string validators, as
you might write to check a user's input on a form in a webpage or
check the format of an externally-provided data file.
Validators in a single file
interface StringValidator {
isAcceptable(s: string): boolean;
}
// Validators to use
let validators: { [s: string]: StringValidator } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();
// Validators to use
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
Validation.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts
ZipCodeValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
}
Test.ts
// Validators to use
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
Once there are multiple files involved, we'll need to make sure all of
the compiled code gets loaded. There are two ways of doing this.
MyTestPage.html (excerpt)
namespace Shapes {
export namespace Polygons {
export class Triangle {}
export class Square {}
}
}
declare namespace D3 {
export interface Selectors {
select: {
(selector: string): Selection;
(element: EventTarget): Selection;
};
}
It is also worth noting that, for Node.js applications, modules are the
default and we recommended modules over namespaces in modern
code.
The compiler will try to find a .ts , .tsx , and then a .d.ts with the
appropriate path. If a specific file could not be found, then the
compiler will look for an ambient module declaration. Recall that these
need to be declared in a .d.ts file.
myModules.d.ts
myOtherModule.ts
The reference tag here allows us to locate the declaration file that
contains the declaration for the ambient module. This is how the
node.d.ts file that several of the TypeScript samples use is consumed.
Needless Namespacing
If you're converting a program from namespaces to modules, it can
be easy to end up with a file that looks like this:
shapes.ts
The top-level module here Shapes wraps up Triangle and Square for
no reason. This is confusing and annoying for consumers of your
module:
shapeConsumer.ts
shapes.ts
shapeConsumer.ts
option while targeting commonjs or umd , but with TypeScript 1.8 and
later, it's possible to use outFile when targeting amd or system .
Starting with ECMAScript 2015, symbol is a primitive data type, just like
number and string .
Just like strings, symbols can be used as keys for object properties.
let obj = {
[sym]: "value",
};
console.log(obj[sym]); // "value"
class C {
[getClassNameSymbol]() {
return "C";
}
}
method.
Symbol.species
A function valued property that is the constructor function that is
used to create derived objects.
Symbol.split
A regular expression method that splits a string at the indices that
match the regular expression. Called by the String.prototype.split
method.
Symbol.toPrimitive
A method that converts an object to a corresponding primitive value.
Called by the ToPrimitive abstract operation.
Symbol.toStringTag
A String value that is used in the creation of the default string
description of an object. Called by the built-in method
Object.prototype.toString .
Symbol.unscopables
An Object whose own property names are property names that are
excluded from the 'with' environment bindings of the associated
objects.
Triple-slash directives are single-line comments containing a single XML
tag. The contents of the comment are used as compiler directives.
They also serve as a method to order the output when using --out or
--outFile . Files are emitted to the output file location in the same
order as the input after preprocessing pass.
The process starts with a set of root files; these are the file names
specified on the command-line or in the "files" list in the
tsconfig.json file. These root files are preprocessed in the same order
they are specified. Before a file is added to the list, all triple-slash
references in it are processed, and their targets included. Triple-slash
references are resolved in a depth first manner, in the order they
have been seen in the file.
Errors
It is an error to reference a file that does not exist. It is an error
for a file to have a triple-slash reference to itself.
Using --noResolve
If the compiler flag --noResolve is specified, triple-slash references
are ignored; they neither result in adding new files, nor change the
order of the files provided.
/// <reference types="..." />
Similar to a /// <reference path="..." /> directive, which serves as a
declaration of dependency, a /// <reference types="..." /> directive
declares a dependency on a package.
Use these directives only when you're authoring a d.ts file by hand.
Built-in lib files are referenced in the same fashion as the "lib"
For declaration file authors who rely on built-in types, e.g. DOM APIs
or built-in JS run-time constructors like Symbol or Iterable , triple-
slash-reference lib directives are recommended. Previously these .d.ts
files had to add forward/duplicate declarations of such types.
"foo".padStart(4);
/// <reference no-default-
lib="true"/>
This directive marks a file as a default library. You will see this
comment at the top of lib.d.ts and its different variants.
This directive instructs the compiler to not include the default library
(i.e. lib.d.ts ) in the compilation. The impact here is similar to
passing --noLib on the command line.
Also note that when passing --skipDefaultLibCheck , the compiler will only
skip checking files with /// <reference no-default-lib="true"/> .
/// <amd-module />
By default AMD modules are generated anonymous. This can lead to
problems when other tools are used to process the resulting modules,
such as bundlers (e.g. r.js ).
amdModule.ts
///<amd-module name="NamedModule"/>
export class C {}
amdModule.js
statements instead.
Generated JS code:
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// OK, because of structural typing
p = new Person();
interface Named {
name: string;
}
let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: "Alice", location: "Seattle" };
x = y;
The same rule for assignment is used when checking function call
arguments:
Note that y has an extra location property, but this does not create
an error. Only members of the target type ( Named in this case) are
considered when checking for compatibility.
y = x; // OK
x = y; // Error
with a compatible type. Note that the names of the parameters are
not considered, only their types. In this case, every parameter of x
// Should be OK!
items.forEach((item) => console.log(item));
Now let's look at how return types are treated, using two functions
that differ only by their return type:
x = y; // OK
y = x; // Error, because x() lacks a location property
The type system enforces that the source function's return type be a
subtype of the target type's return type.
Function Parameter Bivariance
When comparing the types of function parameters, assignment succeeds
if either the source parameter is assignable to the target parameter,
or vice versa. This is unsound because a caller might end up being
given a function that takes a more specialized type, but invokes the
function with a less specialized type. In practice, this sort of error is
rare, and allowing this enables many common JavaScript patterns. A
brief example:
enum EventType {
Mouse,
Keyboard,
}
interface Event {
timestamp: number;
}
interface MyMouseEvent extends Event {
x: number;
y: number;
}
interface MyKeyEvent extends Event {
keyCode: number;
}
// Still disallowed (clear error). Type safety enforced for wholly incompatible
types
listenEvent(EventType.Mouse, (e: number) => console.log(e));
You can have TypeScript raise errors when this happens via the compiler
flag strictFunctionTypes .
Optional Parameters and Rest
Parameters
When comparing functions for compatibility, optional and required
parameters are interchangeable. Extra optional parameters of the
source type are not an error, and optional parameters of the target
type without corresponding parameters in the source type are not an
error.
enum Status {
Ready,
Waiting,
}
enum Color {
Red,
Blue,
Green,
}
class Animal {
feet: number;
constructor(name: string, numFeet: number) {}
}
class Size {
feet: number;
constructor(numFeet: number) {}
}
let a: Animal;
let s: Size;
a = s; // OK
s = a; // OK
Private and protected
members in classes
Private and protected members in a class affect their compatibility.
When an instance of a class is checked for compatibility, if the target
type contains a private member, then the source type must also contain
a private member that originated from the same class. Likewise, the
same applies for an instance with a protected member. This allows a
class to be assignment compatible with its super class, but not with
classes from a different inheritance hierarchy which otherwise have the
same shape.
Generics
Because TypeScript is a structural type system, type parameters only
affect the resulting type when consumed as part of the type of a
member. For example,
interface Empty<T> {}
let x: Empty<number>;
let y: Empty<string>;
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
In this way, a generic type that has its type arguments specified acts
just like a non-generic type.
For generic types that do not have their type arguments specified,
compatibility is checked by specifying any in place of all unspecified
type arguments. The resulting types are then checked for compatibility,
just as in the non-generic case.
For example,
let identity = function <T>(x: T): T {
// ...
};
identity = reverse; // OK, because (x: any) => any matches (y: any) => any
Advanced Topics
Subtype vs Assignment
So far, we've used "compatible", which is not a term defined in the
language spec. In TypeScript, there are two kinds of compatibility:
subtype and assignment. These differ only in that assignment extends
subtype compatibility with rules to allow assignment to and from any ,
and to and from enum with corresponding numeric values.
When a type inference is made from several expressions, the types of those
expressions are used to calculate a "best common type". For example,
```ts twoslash
let x = [0, 1, null];
// ^?
Because the best common type has to be chosen from the provided
candidate types, there are some cases where types share a common
structure, but no one type is the super type of all candidate types.
For example:
```ts twoslash
// @strict: false
class Animal {}
class Rhino extends Animal {
hasHorn: true;
}
class Elephant extends Animal {
hasTrunk: true;
}
class Snake extends Animal {
hasLegs: false;
}
// ---cut---
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
// ^?
However, this code will log undefined , since uiEvent has no property
called button .
var a = 10;
function f() {
var message = "Hello, world!";
return message;
}
and we can also access those same variables within other functions:
function f() {
var a = 10;
return function g() {
var b = a + 1;
return b;
};
}
var g = f();
g(); // returns '11'
a = 2;
var b = g();
a = 3;
return b;
function g() {
return a;
}
}
return x;
}
was declared within the if block, and yet we were able to access it
from outside that block. That's because var declarations are
accessible anywhere within their containing function, module, namespace,
or global scope - all which we'll go over later on - regardless of the
containing block. Some people call this var -scoping or function-
scoping. Parameters are also function scoped.
These scoping rules can cause several types of mistakes. One problem
they exacerbate is the fact that it is not an error to declare the same
variable multiple times:
function sumMatrix(matrix: number[][]) {
var sum = 0;
for (var i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (var i = 0; i < currentRow.length; i++) {
sum += currentRow[i];
}
}
return sum;
}
10
10
10
10
10
10
10
10
10
10
Let's take a minute to consider what that means. setTimeout will run a
function after some number of milliseconds, but only after the for
loop has stopped executing; By the time the for loop has stopped
executing, the value of i is 10 . So each time the given function
gets called, it will print out 10 !
The key difference is not in the syntax, but in the semantics, which
we'll now dive into.
Block-scoping
When a variable is declared using let , it uses what some call lexical-
scoping or block-scoping. Unlike variables declared with var whose
scopes leak out to their containing function, block-scoped variables are
not visible outside of their nearest containing block or for -loop.
if (input) {
// Still okay to reference 'a'
let b = a + 1;
return b;
}
statement's block.
try {
throw "oh no!";
} catch (e) {
console.log("Oh well.");
}
function foo() {
// okay to capture 'a'
return a;
}
let a;
function f(x) {
var x;
var x;
if (true) {
var x;
}
}
let x = 10;
let x = 20; // error: can't re-declare 'x' in the same scope
function f(x) {
let x = 100; // error: interferes with parameter declaration
}
function g() {
let x = 100;
var x = 100; // error: can't have both declarations of 'x'
}
That's not to say that a block-scoped variable can never be declared
with a function-scoped variable. The block-scoped variable just needs
to be declared within a distinctly different block.
function f(condition, x) {
if (condition) {
let x = 100;
return x;
}
return x;
}
return sum;
}
This version of the loop will actually perform the summation correctly
because the inner loop's i shadows i from the outer loop.
function theCityThatAlwaysSleeps() {
let getCity;
if (true) {
let city = "Seattle";
getCity = function () {
return city;
};
}
return getCity();
}
Because we've captured city from within its environment, we're still
able to access it despite the fact that the if block finished
executing.
0
1
2
3
4
5
6
7
8
9
const declarations
const declarations are another way of declaring variables.
const numLivesForCat = 9;
They are like let declarations but, as their name implies, their value
cannot be changed once they are bound. In other words, they have
the same scoping rules as let , but you can't re-assign to them.
This should not be confused with the idea that the values they refer
to are immutable.
const numLivesForCat = 9;
const kitty = {
name: "Aurora",
numLives: numLivesForCat,
};
// Error
kitty = {
name: "Danielle",
numLives: numLivesForCat,
};
// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;
Unless you take specific measures to avoid it, the internal state of a
const variable is still modifiable. Fortunately, TypeScript allows you to
specify that members of an object are readonly . The chapter on
Interfaces has the details.
let vs. const
Given that we have two types of declarations with similar scoping
semantics, it's natural to find ourselves asking which one to use. Like
most broad questions, the answer is: it depends.
Use your best judgement, and if applicable, consult the matter with
the rest of your team.
This creates two new variables named first and second . This is
equivalent to using indexing, but is much more convenient:
first = input[0];
second = input[1];
// swap variables
[first, second] = [second, first];
You can create a variable for the remaining items in a list using the
syntax ... :
Or other elements:
As with arrays, you can destructure the rest of the tuple with ... , to
get a shorter tuple:
let o = {
a: "foo",
b: 12,
c: "bar",
};
let { a, b } = o;
This creates new variables a and b from o.a and o.b . Notice that
you can skip c if you don't need it.
You can create a variable for the remaining items in an object using
the syntax ... :
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
Property renaming
You can also give different names to properties:
Confusingly, the colon here does not indicate the type. The type, if
you specify it, still needs to be written after the entire destructuring:
Default values
Default values let you specify a default value in case a property is
undefined:
Object spread also has a couple of other surprising limits. First, it only
includes an objects' own, enumerable properties. Basically, that means
you lose methods when you spread instances of an object:
class C {
p = 12;
m() {}
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!
setVerticalAlignment("middel");
This is pretty nice because string literal types can basically spell-check our
string values.
We also like that string literals can be used as property names in mapped types.
In this sense, they're also usable as building blocks:
```ts
type Options = {
[K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean;
};
// same as
// type Options = {
// noImplicitAny?: boolean,
// strictNullChecks?: boolean,
// strictFunctionTypes?: boolean
// };
But there's another place that that string literal types could be used
as building blocks: building other string literal types.
That's why TypeScript 4.1 brings the template literal string type. It
has the same syntax as template literal strings in JavaScript, but is
used in type positions. When you use it with concrete literal types, it
produces a new string literal type by concatenating the contents.
```ts twoslash type World = "world";
```ts twoslash
type Color = "red" | "blue";
type Quantity = "one" | "two";
This can be used beyond cute examples in release notes. For example,
several libraries for UI components have a way to specify both vertical
and horizontal alignment in their APIs, often with both at once using a
single string like "bottom-right" . Between vertically aligning with "top" ,
"middle" , and "bottom" , and horizontally aligning with "left" ,
"center" , and "right" , there are 9 possible strings where each of the
former strings is connected with each of the latter strings using a
dash.
Some of the real value comes from dynamically creating new string literals.
For example, imagine a `makeWatchedObject` API that takes an object and produces a
mostly identical object, but with a new `on` method to detect for changes to the
properties.
```ts
let person = makeWatchedObject({
firstName: "Homer",
age: 42, // give-or-take
location: "Springfield",
});
person.on("firstNameChanged", () => {
console.log(`firstName was changed!`);
});
/// Create a "watched object" with an 'on' method /// so that you
can watch for changes to properties. declare function
makeWatchedObject(obj: T): T & PropEventSource;
With this, we can build something that errors when we give the wrong property!
```ts twoslash
// @errors: 2345
type PropEventSource<T> = {
on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
};
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
let person = makeWatchedObject({
firstName: "Homer",
age: 42, // give-or-take
location: "Springfield",
});
// ---cut---
// error!
person.on("firstName", () => {});
// error!
person.on("frstNameChanged", () => {});
``ts twoslash type PropEventSource<T> = { on<K extends string & keyof T>
(eventName: ${K}Changed`, callback: (newValue: T[K]) => void ): void; };
```ts twoslash
type EnthusiasticGreeting<T extends string> = `${Uppercase<T>}`
For more details, see the original pull request and the in-progress pull
request to switch to type alias helpers.
Key Remapping in Mapped
Types
Just as a refresher, a mapped type can create new object types based
on arbitrary keys
type Options = {
[K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean;
};
// same as
// type Options = {
// noImplicitAny?: boolean,
// strictNullChecks?: boolean,
// strictFunctionTypes?: boolean
// };
/// 'Partial<T>' is the same as 'T', but with each property marked optional.
type Partial<T> = {
[K in keyof T]?: T[K];
};
Until now, mapped types could only produce new object types with keys
that you provided them; however, lots of the time you want to be able
to create new keys, or filter out keys, based on the inputs.
That's why TypeScript 4.1 allows you to re-map keys in mapped types
with a new as clause.
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
// ^^^^^^^^^^^^^
// This is the new syntax!
}
With this new as clause, you can leverage features like template literal
types to easily create property names based off of old ones.
```ts twoslash
// Remove the 'kind' property
type RemoveKindField<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K]
};
interface Circle {
kind: "circle";
radius: number;
}
For more information, take a look at the original pull request over on
GitHub.
Recursive Conditional Types
In JavaScript it's fairly common to see functions that can flatten and
build up container types at arbitrary levels. For example, consider the
.then() method on instances of Promise . .then(...) unwraps each
promise until it finds a value that's not "promise-like", and passes
that value to a callback. There's also a relatively new flat method on
Array s that can take a depth of how deep to flatten.
Keep in mind that while these recursive types are powerful, but they
should be used responsibly and sparingly.
First off, these types can do a lot of work which means that they can
increase type-checking time. Trying to model numbers in the Collatz
conjecture or Fibonacci sequence might be fun, but don't ship that in
.d.ts files on npm.
But apart from being computationally intensive, these types can hit an
internal recursion depth limit on sufficiently-complex inputs. When that
recursion limit is hit, that results in a compile-time error. In general,
it's better not to use these types at all than to write something that
fails on more realistic examples.
// These are all allowed too! // They have the type 'string |
number'. opts.yadda.toString(); opts["foo bar baz"].toString();
opts[Math.random()].toString(); }
In the above example, `Options` has an index signature that says any accessed
property that's not already listed should have the type `string | number`.
This is often convenient for optimistic code that assumes you know what you're
doing, but the truth is that most values in JavaScript do not support every
potential property name.
Most types will not, for example, have a value for a property key created by
`Math.random()` like in the previous example.
For many users, this behavior was undesirable, and felt like it wasn't leveraging
the full strict-checking of `--strictNullChecks`.
That's why TypeScript 4.1 ships with a new flag called `--
noUncheckedIndexedAccess`.
Under this new mode, every property access (like `foo.bar`) or indexed access (like
`foo["bar"]`) is considered potentially undefined.
That means that in our last example, `opts.yadda` will have the type `string |
number | undefined` as opposed to just `string | number`.
If you need to access that property, you'll either have to check for its existence
first or use a non-null assertion operator (the postfix `!` character).
This flag can be handy for catching out-of-bounds errors, but it might be noisy for
a lot of code, so it is not automatically enabled by the `--strict` flag; however,
if this feature is interesting to you, you should feel free to try it and determine
whether it makes sense for your team's codebase!
Using path-mapping is fairly common - often it's to have nicer imports, often it's
to simulate monorepo linking behavior.
Previously if you were starting a checked JavaScript project, you had to set both
`allowJs` and `checkJs`.
This was a slightly annoying bit of friction in the experience, so `checkJs` now
implies `allowJs` by default.
[See more details at the pull request]
(https://github.com/microsoft/TypeScript/pull/40275).
TypeScript 4.1 supports React 17's upcoming `jsx` and `jsxs` factory functions
through two new options for the `jsx` compiler option:
- `react-jsx`
- `react-jsxdev`
These options are intended for production and development compiles respectively.
Often, the options from one can extend from the other.
## TypeScript 4.0
Consider a function in JavaScript called `concat` that takes two array or tuple
types and concatenates them together to make a new array.
```js
function concat(arr1, arr2) {
return [...arr1, ...arr2];
}
Also consider tail , that takes an array or tuple, and returns all
elements but the first.
function tail(arg) {
const [_, ...result] = arg;
return result;
}
For concat , the only valid thing we could do in older versions of the
language was to try and write some overloads.
function concat(arr1: [], arr2: []): [];
function concat<A>(arr1: [A], arr2: []): [A];
function concat<A, B>(arr1: [A, B], arr2: []): [A, B];
function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C];
function concat<A, B, C, D>(arr1: [A, B, C, D], arr2: []): [A, B, C, D];
function concat<A, B, C, D, E>(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E];
function concat<A, B, C, D, E, F>(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D,
E, F];)
But that signature doesn't encode anything about the lengths of the
input, or the order of the elements, when using tuples.
For example, that means we can type function like tail , without our
"death by a thousand overloads" issue.
const r1 = tail(myTuple); // ^?
The second change is that rest elements can occur anywhere in a tuple - not just at
the end!
```ts
type Strings = [string, string];
type Numbers = [number, number];
While that one signature is still a bit lengthy, it's just one signature that
doesn't have to be repeated, and it gives predictable behavior on all arrays and
tuples.
```js
function partialCall(f, ...headArgs) {
return (...tailArgs) => f(...headArgs, ...tailArgs);
}
TypeScript 4.0 improves the inference process for rest parameters and
rest tuple elements so that we can type this and have it "just work".
```ts twoslash
// @errors: 2345 2554 2554 2345
type Arr = readonly unknown[];
// This works!
const f3 = partialCall(foo, "hello");
// ^?
// Works!
f3(123, true);
f3();
f3(123, "hello");
l d l l t
Labeled Tuple Elements
Improving the experience around tuple types and parameter lists is
important because it allows us to get strongly typed validation around
common JavaScript idioms - really just slicing and dicing argument lists
and passing them to other functions. The idea that we can use tuple
types for rest parameters is one place where this is crucial.
For example, the following function that uses a tuple type as a rest
parameter…
There is one place where the differences begin to become observable though:
readability.
In the first example, we have no parameter names for the first and second elements.
While these have no impact on type-checking, the lack of labels on tuple positions
can make them harder to use - harder to communicate our intent.
That's why in TypeScript 4.0, tuples types can now provide labels.
```ts
type Range = [start: number, end: number];
To deepen the connection between parameter lists and tuple types, the
syntax for rest elements and optional elements mirrors the syntax for
parameter lists.
There are a few rules when using labeled tuples. For one, when
labeling a tuple element, all other elements in the tuple must also be
labeled.
It's worth noting - labels don't require us to name our variables differently when
destructuring.
They're purely there for documentation and tooling.
```ts twoslash
function foo(x: [first: string, second: number]) {
// ...
```ts twoslash class Square { // Previously both of these were any area;
// ^? sideLength; // ^? constructor(sideLength: number) {
this.sideLength = sideLength; this.area = sideLength ** 2; } }
In cases where not all paths of a constructor assign to an instance member, the
property is considered to potentially be `undefined`.
<!--prettier-ignore -->
```ts twoslash
// @errors: 2532
class Square {
sideLength;
// ^?
constructor(sideLength: number) {
if (Math.random()) {
this.sideLength = sideLength;
}
}
get area() {
return this.sideLength ** 2;
}
}
In cases where you know better (e.g. you have an initialize method
of some sort), you'll still need an explicit type annotation along with a
definite assignment assertion ( ! ) if you're in
strictPropertyInitialization .
```ts twoslash class Square { // definite assignment assertion // v
sideLength!: number; // ^^^^^^^^ // type annotation
```ts
// Addition
// a = a + b
a += b;
// Subtraction
// a = a - b
a -= b;
// Multiplication
// a = a * b
a *= b;
// Division
// a = a / b
a /= b;
// Exponentiation
// a = a ** b
a **= b;
These operators are great for substituting any example where a user
might write code like the following:
a = a && b;
a = a || b;
a = a ?? b;
There are even some patterns we've seen (or, uh, written ourselves)
to lazily initialize values, only if they'll be needed.
// After
(values ??= []).push("hello");
On the rare case that you use getters or setters with side-effects,
it's worth noting that these operators only perform assignments if
necessary. In that sense, not only is the right side of the operator
"short-circuited" - the assignment itself is too.
obj.prop ||= foo();
if (!obj.prop) {
obj.prop = foo();
}
Try running the following example to see how that differs from always
performing the assignment.
// Replace me!
return Math.random() < 0.5;
},
set prop(_val: boolean) {
console.log("setter has run");
}
};
For more details, you can [take a look at the pull request here]
(https://github.com/microsoft/TypeScript/pull/37727).
You can also [check out TC39's proposal repository for this feature]
(https://github.com/tc39/proposal-logical-assignment/).
Since the beginning days of TypeScript, `catch` clause variables have always been
typed as `any`.
This meant that TypeScript allowed you to do anything you wanted with them.
```ts twoslash
try {
// Do some work
} catch (x) {
// x has type 'any' - have fun!
console.log(x.message);
console.log(x.toUpperCase());
x++;
x.yadda.yadda.yadda();
}
That's why TypeScript 4.0 now lets you specify the type of catch
For more details you can [peek at the changes for this feature]
(https://github.com/microsoft/TypeScript/pull/39015).
In TypeScript 4.0, users can customize the fragment factory through the new
`jsxFragmentFactory` option.
```json5
{
compilerOptions: {
target: "esnext",
module: "commonjs",
jsx: "react",
jsxFactory: "h",
jsxFragmentFactory: "Fragment",
},
}
```tsx twoslash
// @noErrors
// @showEmit
// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */
We'd like to extend a big thanks to community member Noj Vek for
sending this pull request and patiently working with our team on it.
You can see that the pull request for more details!
TypeScript 3.9
Improvements in Inference
and Promise.all
Recent versions of TypeScript (around 3.7) have had updates to the
declarations of functions like Promise.all and Promise.race .
Unfortunately, that introduced a few regressions, especially when mixing
in values with null or undefined .
interface Lion {
roar(): void;
}
interface Seal {
singKissFromARose(): void;
}
Thanks to a pull request from Jack Bates, this has been fixed with
improvements in our inference process in TypeScript 3.9. The above no
longer errors. If you've been stuck on older versions of TypeScript
due to issues around Promise s, we encourage you to give 3.9 a shot!
t t t
What About the awaited Type?
If you've been following our issue tracker and design meeting notes,
you might be aware of some work around a new type operator called
awaited . This goal of this type operator is to accurately model the
way that Promise unwrapping works in JavaScript.
https://github.com/microsoft/TypeScript/pull/36576
https://github.com/microsoft/TypeScript/pull/36590
https://github.com/microsoft/TypeScript/pull/36607
https://github.com/microsoft/TypeScript/pull/36622
https://github.com/microsoft/TypeScript/pull/36754
https://github.com/microsoft/TypeScript/pull/36696
While there's still room for improvement, we hope this work translates
to a snappier experience for everyone!
// @ts-expect-error Comments
Imagine that we're writing a library in TypeScript and we're exporting
some function called doStuff as part of our public API. The
function's types declare that it takes two string s so that other
TypeScript users can get type-checking errors, but it also does a
runtime check (maybe only in development builds) to give JavaScript
users a helpful error.
// do some stuff
}
expect(() => {
doStuff(123, 456);
}).toThrow();
doStuff(123, 456);
// ~~~
// error: Type 'number' is not assignable to type 'string'.
// @ts-expect-error
console.log(47 * "octopus");
// @ts-expect-error
console.log(1 + 1);
ts-ignore or ts-expect-error ?
In some ways // @ts-expect-error can act as a suppression comment,
similar to // @ts-ignore . The difference is that // @ts-ignore will do
nothing if the following line is error-free.
you're writing test code where you actually want the type system
to error on an operation
you expect a fix to be coming in fairly quickly and you just need
a quick workaround
you're in a reasonably-sized project with a proactive team that
wants to remove suppression comments as soon affected code is
valid again
you have an a larger project and and new errors have appeared in
code with no clear owner
you are in the middle of an upgrade between two different
versions of TypeScript, and a line of code errors in one version
but not another.
you honestly don't have the time to decide which of these options
is better.
Uncalled Function Checks in
Conditional Expressions
In TypeScript 3.7 we introduced uncalled function checks to report an
error when you've forgotten to call a function.
// Oops!
if (hasImportantPermissions) {
// ~~~~~~~~~~~~~~~~~~~~~~~
// This condition will always return true since the function is always defined.
// Did you mean to call it instead?
deleteAllTheImportantFiles();
}
falseExpr syntax).
declare function listFilesOfDirectory(dirPath: string): string[];
declare function isDirectory(): boolean;
https://github.com/microsoft/TypeScript/issues/36048
Editor Improvements
The TypeScript compiler not only powers the TypeScript editing
experience in most major editors, it also powers the JavaScript
experience in the Visual Studio family of editors and more. Using new
TypeScript/JavaScript functionality in your editor will differ depending
on your editor, but
CommonJS Auto-Imports in
JavaScript
One great new improvement is in auto-imports in JavaScript files using
CommonJS modules.
const fs = require("fs");
TypeScript now automatically detects the types of imports you're using
to keep your file's style clean and consistent.
For more details on the change, see the corresponding pull request.
/*start*/
for (let i = 0; i <= maxValue; i++) {
// First get the squared value.
let square = i ** 2;
printSquares();
function printSquares() {
for (let i = 0; i <= maxValue; i++) {
// First get the squared value.
let square = i ** 2;
// Now print the squared value.
console.log(square);
}
}
That's not ideal - we had a blank line between each statement in our
for loop, but the refactoring got rid of it! TypeScript 3.9 does a
little more work to preserve what we write.
const maxValue = 100;
printSquares();
function printSquares() {
for (let i = 0; i <= maxValue; i++) {
// First get the squared value.
let square = i ** 2;
You can see more about the implementation in this pull request
One case where this slightly fell over is when a tsconfig.json simply
existed to reference other tsconfig.json files.
This file that really does nothing but manage other project files is often called a
"solution" in some environments.
Here, none of these `tsconfig.*.json` files get picked up by the server, but we'd
really like the language server to understand that the current `.ts` file probably
belongs to one of the mentioned projects in this root `tsconfig.json`.
## Breaking Changes
TypeScript recently implemented the optional chaining operator, but we've received
user feedback that the behavior of optional chaining (`?.`) with the non-null
assertion operator (`!`) is extremely counter-intuitive.
```ts
foo?.bar!.baz;
(foo?.bar).baz;
In other words, most people felt that the original snippet should be
interpreted as
foo?.bar.baz;
This is a breaking change, but we believe most code was written with
the new interpretation in mind. Users who want to revert to the old
behavior can add explicit parentheses around the left side of the !
operator.
foo?.bar!.baz;
Luckily, thanks to the pull request enforcing this from Brad Zacher,
you'll get an error message along the lines of
For example:
let directions = <span>Navigate to: Menu Bar > Tools > Options</div>
// ~ ~
// Unexpected token. Did you mean `{'>'}` or `>`?
That error message came with a handy quick fix, and thanks to
Alexander Tarasyuk, you can apply these changes in bulk if you have a
lot of errors.
interface A {
a: number; // notice this is 'number'
}
interface B {
b: string;
}
interface C {
a?: boolean; // notice this is 'boolean'
b: string;
}
y = x;
Intersections Reduced By
Discriminant Properties
There are a few cases where you might end up with types that
describe values that just don't exist. For example
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
Thanks to a pull request from GitHub user pathurs, TypeScript 3.9 now
conforms more closely with ECMAScript in this regard.
TypeScript 3.8 adds a new syntax for type-only imports and exports.
It's important to note that classes have a value at runtime and a type
at design-time, and the use is context-sensitive. When using import
type to import a class, you can't do things like extend from it.
import type { Component } from "react";
interface ButtonProps {
// ...
}
If you've used Flow before, the syntax is fairly similar. One difference
is that we've added a few restrictions to avoid code that might
appear ambiguous.
option), but will error when a value import is only used as a type.
This might be useful if you want to ensure no values are being
accidentally imported, but still make side-effect imports explicit.
For more information about the feature, you can take a look at the
pull request, and relevant changes around broadening where imports
from an import type declaration can be used.
ECMAScript Private Fields
TypeScript 3.8 brings support for ECMAScript's private fields, part of
the stage-3 class fields proposal.
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
greet() {
console.log(`Hello, my name is ${this.#name}!`);
}
}
jeremy.#name;
// ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.
class C {
foo = 10;
cHelper() {
return this.foo;
}
}
class D extends C {
foo = 20;
dHelper() {
return this.foo;
}
}
With private fields, you'll never have to worry about this, since each
field name is unique to the containing class.
class C {
#foo = 10;
cHelper() {
return this.#foo;
}
}
class D extends C {
#foo = 20;
dHelper() {
return this.#foo;
}
}
class Square {
#sideLength: number;
constructor(sideLength: number) {
this.#sideLength = sideLength;
}
equals(other: any) {
return this.#sideLength === other.#sideLength;
}
}
// Boom!
// TypeError: attempted to get private field on non-instance
// This fails because 'b' is not an instance of 'Square'.
console.log(a.equals(b));
Finally, for any plain .js file users, private fields always have to be
declared before they're assigned to.
class C {
// No declaration for '#foo'
// :(
constructor(foo: number) {
// SyntaxError!
// '#foo' needs to be declared before writing to it.
this.#foo = foo;
}
}
class C {
/** @type {number} */
#foo;
constructor(foo: number) {
// This works.
this.#foo = foo;
}
}
For more information about the implementation, you can check out the
original pull request
class C {
private foo = 10;
}
The upside is that this sort of "soft privacy" can help your consumers
temporarily work around not having access to some API, and also works
in any runtime.
class C {
#foo = 10;
}
One more thing to think about is where you intend for your code to
run. TypeScript currently can't support this feature unless targeting
ECMAScript 2015 (ES6) targets or higher. This is because our
downleveled implementation uses WeakMap s to enforce privacy, and
WeakMap s can't be polyfilled in a way that doesn't cause memory leaks.
In contrast, TypeScript's private -declared properties work with all
targets - even ECMAScript 3!
Note there's a subtlety: top-level await only works at the top level
of a module, and files are only considered modules when TypeScript
finds an import or an export . In some basic cases, you might need to
write out export {} as some boilerplate to make sure of this.
Top level await may not work in all environments where you might
expect at this point. Currently, you can only use top level await when
the target compiler option is es2017 or above, and module is esnext
or system . Support within several environments and bundlers may be
limited or may require enabling experimental support.
For more information on our implementation, you can check out the
original pull request.
es2020 for target and
module
TypeScript 3.8 supports es2020 as an option for module and target .
This will preserve newer ECMAScript 2020 features like optional
chaining, nullish coalescing, export * as ns , and dynamic import(...)
syntax. It also means bigint literals now have a stable target below
esnext .
JSDoc Property Modifiers
TypeScript 3.8 supports JavaScript files by turning on the allowJs
flag, and also supports type-checking those JavaScript files via the
checkJs option or by adding a // @ts-check comment to the top of
your .js files.
// @ts-check
class Foo {
constructor() {
/** @private */
this.stuff = 100;
}
printStuff() {
console.log(this.stuff);
}
}
new Foo().stuff;
// ~~~~~
// error! Property 'stuff' is private and only accessible within class 'Foo'.
@public is always implied and can be left off, but means that a
property can be reached from anywhere.
@private means that a property can only be used within the
containing class.
@protected means that a property can only be used within the
containing class, and all derived subclasses, but not on dissimilar
instances of the containing class.
// @ts-check
class Foo {
constructor() {
/** @readonly */
this.stuff = 100;
}
writeToStuff() {
this.stuff = 200;
// ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.
}
}
new Foo().stuff++;
// ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.
Better Directory Watching on
Linux and watchOptions
TypeScript 3.8 ships a new strategy for watching directories, which is
crucial for efficiently picking up changes to node_modules .
} }
`watchOptions` contains 4 new options that can be configured to handle how
TypeScript keeps track of changes.
For more information on these changes, [head over to GitHub to see the pull
request](https://github.com/microsoft/TypeScript/pull/35615) to read more.
In a codebase like Visual Studio Code, this reduced rebuild times for changes in
certain files from about 14 seconds to about 1 second.
While we don't necessarily recommend this option for all codebases, you might be
interested if you have an extremely large codebase and are willing to defer full
project errors until later (e.g. a dedicated build via a `tsconfig.fullbuild.json`
or in CI).
For more details, you can [see the original pull request]
(https://github.com/microsoft/TypeScript/pull/35711).
## TypeScript 3.7
## Optional Chaining
[Playground](/play/#example/optional-chaining)
At its core, optional chaining lets us write code where TypeScript can immediately
stop running some expressions if we run into a `null` or `undefined`.
The star of the show in optional chaining is the new `?.` operator for _optional
property accesses_.
When we write code like
```ts
let x = foo?.bar.baz();
this is a way of saying that when foo is defined, foo.bar.baz() will be
computed; but when foo is null or undefined , stop what we're doing
and just return undefined ."
More plainly, that code snippet is the same as writing the following.
Note that if bar is null or undefined , our code will still hit an error
accessing baz . Likewise, if baz is null or undefined , we'll hit an
error at the call site. ?. only checks for whether the value on the
left of it is null or undefined - not any of the subsequent
properties.
// Before
if (foo && foo.bar && foo.bar.baz) {
// ...
}
// After-ish
if (foo?.bar?.baz) {
// ...
}
Keep in mind that ?. acts differently than those && operations since
&& will act specially on "falsy" values (e.g. the empty string, 0 ,
NaN , and, well, false ), but this is an intentional feature of the
construct. It doesn't short-circuit on valid data like 0 or empty
strings.
Optional chaining also includes two other operations. First there's the
optional element access which acts similarly to optional property
accesses, but allows us to access non-identifier properties (e.g.
arbitrary strings, numbers, and symbols):
/**
* Get the first element of the array if we have an array.
* Otherwise return undefined.
*/
function tryGetFirstElement<T>(arr?: T[]) {
return arr?.[0];
// equivalent to
// return (arr === null || arr === undefined) ?
// undefined :
// arr[0];
}
return result;
}
let temp = foo === null || foo === undefined ? undefined : foo.bar;
More more details, you can read up on the proposal and view the
original pull request.
Nullish Coalescing
Playground
this is a new way to say that the value foo will be used when it's
"present"; but when it's null or undefined , calculate bar() in its
place.
let x = foo !== null && foo !== undefined ? foo : bar();
function initializeAudio() {
let volume = localStorage.volume || 0.5;
// ...
}
When localStorage.volume is set to 0 , the page will set the volume to
0.5 which is unintended. ?? avoids some unintended behavior from
0 , NaN and "" being treated as falsy values.
function multiply(x, y) {
assert(typeof x === "number");
assert(typeof y === "number");
return x * y;
}
function yell(str) {
assert(typeof str === "string");
return str.toUppercase();
// Oops! We misspelled 'toUpperCase'.
// Would be great if TypeScript still caught this!
}
The alternative was to instead rewrite the code so that the language
could analyze it, but this isn't convenient.
function yell(str) {
if (typeof str !== "string") {
throw new TypeError("str should have been a string.");
}
// Error caught!
return str.toUppercase();
}
The first type of assertion signature models the way that Node's
assert function works. It ensures that whatever condition is being
checked must be true for the remainder of the containing scope.
asserts condition says that whatever gets passed into the condition
return str.toUppercase();
// ~~~~~~~~~~~
// error: Property 'toUppercase' does not exist on type 'string'.
// Did you mean 'toUpperCase'?
}
Here asserts val is string ensures that after any call to assertIsString ,
any variable passed in will be known to be a string .
return str.toUppercase();
// ~~~~~~~~~~~
// error: Property 'toUppercase' does not exist on type 'string'.
// Did you mean 'toUpperCase'?
}
And just like type predicate signatures, these assertion signatures are
incredibly expressive. We can express some fairly sophisticated ideas
with these.
To read up more about assertion signatures, check out the original pull
request.
Better Support for never -
Returning Functions
As part of the work for assertion signatures, TypeScript needed to
encode more about where and which functions were being called. This
gave us the opportunity to expand support for another class of
functions: functions that return never .
As with assertion functions, you can read up more at the same pull
request.
(More) Recursive Type Aliases
Playground
This is fairly consistent with how other languages treat type aliases,
but it does give rise to some slightly surprising scenarios for how
users leverage the feature. For example, in TypeScript 3.6 and prior,
the following causes an error.
This is strange because there is technically nothing wrong with any use
users could always write what was effectively the same code by
introducing an interface.
This means that code like the following that was trying to represent
JSON…
interface JsonObject {
[property: string]: Json;
}
type Json =
| string
| number
| boolean
| null
| { [property: string]: Json }
| Json[];
For more information, you can read up on the original pull request.
--declaration and --allowJs
The --declaration flag in TypeScript allows us to generate .d.ts files
(declaration files) from TypeScript source files (i.e. .ts and .tsx
The most impactful outcome of this feature might a bit subtle: with
TypeScript 3.7, users can write libraries in JSDoc annotated JavaScript
and support TypeScript users.
The way that this works is that when using allowJs , TypeScript has
some best-effort analyses to understand common JavaScript patterns;
however, the way that some patterns are expressed in JavaScript don't
necessarily look like their equivalents in TypeScript. When declaration
emit is turned on, TypeScript figures out the best way to transform
JSDoc comments and CommonJS exports into valid type declarations
and the like in the output .d.ts files.
module.exports.blurImage = blurImage;
/**
* Produces a blurred image from an input buffer.
*
* @param input {Uint8Array}
* @param width {number}
* @param height {number}
*/
function blurImage(input, width, height) {
const numPixels = width * height * 4;
assert(input.length === numPixels);
const result = new Uint8Array(numPixels);
// TODO
return result;
}
/**
* Produces a blurred image from an input buffer.
*
* @param input {Uint8Array}
* @param width {number}
* @param height {number}
*/
export function blurImage(
input: Uint8Array,
width: number,
height: number
): Uint8Array;
This can go beyond basic functions with @param tags too, where the
following example:
/**
* @callback Job
* @returns {void}
*/
For more details, you can check out the original pull request.
The useDefineForClassFields
Flag and The declare
Property Modifier
Back when TypeScript implemented public class fields, we assumed to
the best of our abilities that the following code
class C {
foo = 100;
bar: string;
}
class C {
constructor() {
this.foo = 100;
}
}
While TypeScript 3.7 isn't changing any existing emit by default, we've
been rolling out changes incrementally to help users mitigate potential
future breakage. We've provided a new flag called
useDefineForClassFields to enable this emit mode with some new checking
logic.
This can cause quite a bit of fallout for existing code that use
inheritance. First of all, set accessors from base classes won't get
triggered - they'll be completely overwritten.
class Base {
set data(value: string) {
console.log("data changed to " + value);
}
}
interface Animal {
animalStuff: any;
}
interface Dog extends Animal {
dogStuff: any;
}
class AnimalHouse {
resident: Animal;
constructor(animal: Animal) {
this.resident = animal;
}
}
constructor(dog: Dog) {
super(dog);
}
}
What these two boil down to is that mixing properties with accessors is
going to cause issues, and so will re-declaring properties with no
initializers.
To detect the issue around accessors, TypeScript 3.7 will now emit
get / set accessors in .d.ts files so that in TypeScript can check for
overridden accessors.
Code that's impacted by the class fields change can get around the
issue by converting field initializers to assignments in constructor
bodies.
class Base {
set data(value: string) {
console.log("data changed to " + value);
}
}
To help mitigate the second issue, you can either add an explicit
initializer or add a declare modifier to indicate that a property should
have no emit.
interface Animal {
animalStuff: any;
}
interface Dog extends Animal {
dogStuff: any;
}
class AnimalHouse {
resident: Animal;
constructor(animal: Animal) {
this.resident = animal;
}
}
constructor(dog: Dog) {
super(dog);
}
}
For more information, you can take a look at the original pull request
for these changes.
You can read up more about this change by reading up on its pull
request.
Uncalled Function Checks
A common and dangerous error is to forget to invoke a function,
especially if the function has zero arguments or is named in a way that
implies it might be a property rather than a function.
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
// later...
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
If you intended to test the function without calling it, you can
correct the definition of it to include undefined / null , or use !! to
write something like if (!!user.isAdministrator) to indicate that the
coercion is intentional.
We owe a big thanks to GitHub user @jwbay who took the initiative to
create a proof-of-concept and iterated to provide us with with the
current version.
// @ts-nocheck in TypeScript
Files
TypeScript 3.7 allows us to add // @ts-nocheck comments to the top of
TypeScript files to disable semantic checks. Historically this comment was
only respected in JavaScript source files in the presence of checkJs ,
but we've expanded support to TypeScript files to make migrations
easier for all users.
Semicolon Formatter Option
TypeScript's built-in formatter now supports semicolon insertion and
removal at locations where a trailing semicolon is optional due to
JavaScript's automatic semicolon insertion (ASI) rules. The setting is
available now in Visual Studio Code Insiders, and will be available in
Visual Studio 16.4 Preview 2 in the Tools Options menu.
DOM Changes
Types in lib.dom.d.ts have been updated. These changes are largely
correctness changes related to nullability, but impact will ultimately
depend on your codebase.
// ./someOtherModule.ts
interface SomeType {
y: string;
}
// ./myModule.ts
import { SomeType } from "./someOtherModule";
export interface SomeType {
x: number;
}
function* foo() {
if (Math.random() < 0.5) yield 100;
return "Finished!";
}
function* bar() {
let x: { hello(): void } = yield;
x.hello();
}
In TypeScript 3.6, the checker now knows that the correct type for
curr.value should be string in our first example, and will correctly
error on our call to next() in our last example. This is thanks to
some changes in the Iterator and IteratorResult type declarations to
include a few new type parameters, and to a new type that TypeScript
uses to represent generators called the Generator type.
The Iterator type now allows users to specify the yielded type, the
returned type, and the type that next can accept.
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
function* foo() {
let x: string = yield;
console.log(x.toUpperCase());
}
let x = foo();
x.next(); // first call to 'next' is always ignored
x.next(42); // error! 'number' is not assignable to 'string'
// prints:
//
// 0
// 1
// 2
// 3
// 4
// 5
// DONE!
For more details on the change, see the pull request here.
More Accurate Array Spread
In pre-ES2015 targets, the most faithful emit for constructs like
for / of loops and array spreads can be a bit heavy. For this reason,
TypeScript uses a simpler emit by default that only supports array
types, and supports iterating on other types using the --
works fairly well; however, there were some common cases where the
transformation of array spreads had observable differences. For
example, the following array containing a spread
[...Array(5)];
However, TypeScript would instead transform the original code into this
code:
Array(5).slice();
interface User {
name: string;
age: number;
location: string;
}
// This module:
console.log(import.meta.url);
accessors.
Ambient Classes and Functions
Can Merge
In previous versions of TypeScript, it was an error to merge classes
and functions under any circumstances. Now, ambient classes and
functions (classes/functions with the declare modifier, or in .d.ts
files) can merge. This means that now you can write the following:
For more details on these APIs, you can see the original pull request.
Semicolon-Aware Code Edits
Editors like Visual Studio and Visual Studio Code can automatically apply
quick fixes, refactorings, and other transformations like automatically
importing values from other modules. These transformations are
powered by TypeScript, and older versions of TypeScript
unconditionally added semicolons to the end of every statement;
unfortunately, this disagreed with many users' style guidelines, and
many users were displeased with the editor inserting semicolons.
Speed improvements
TypeScript 3.5 introduces several optimizations around type-checking
and incremental builds.
Type-checking speed-ups
TypeScript 3.5 contains certain optimizations over TypeScript 3.4 for
type-checking more efficiently. These improvements are significantly
more pronounced in editor scenarios where type-checking drives
operations like code completion lists.
--incremental improvements
TypeScript 3.5 improves on 3.4's --incremental build mode, by saving
information about how the state of the world was calculated -
compiler settings, why files were looked up, where files were found,
etc. In scenarios involving hundreds of projects using TypeScript's
project references in --build mode, we've found that the amount of
time rebuilding can be reduced by as much as 68% compared to
TypeScript 3.4!
type Person = {
name: string;
age: number;
location: string;
};
// equivalent to
type QuantumPerson = {
name: string;
age: number;
};
Here we were able to copy over all the properties of Person except
for location using the Omit helper.
For more details, see the pull request on GitHub to add Omit , as well
as the change to use Omit for object rest.
type Label = {
name: string;
};
flag.
This mode adds flexibility for mixing and matching the way 3rd party
libraries, where globals that libraries declare can always be consumed,
even from within modules.
target = source;
interface Bar {
kind: "bar";
value: number;
}
However, this was a bit overly strict for the original example. If you
figure out the precise type of any possible value of S , you can
actually see that it matches the types in T exactly.
For more details, you can see the original pull request on GitHub.
Higher order type inference
from generic constructors
In TypeScript 3.4, we improved inference for when generic functions
that return functions like so:
function compose<T, U, V>(f: (x: T) => U, g: (y: U) => V): (x: T) => V {
return (x) => g(f(x));
}
Instead of a relatively useless type like (x: {}) => Box<{}[]> , which older
versions of the language would infer, TypeScript 3.4's inference allows
newFn to be generic. Its new type is <T>(x: T) => Box<T[]> .
class Bag<U> {
kind: "bag";
value: U;
constructor(value: U) {
this.value = value;
}
}
By default with these settings, when we run `tsc`, TypeScript will look for a file
called `.tsbuildinfo` in the output directory (`./lib`).
If `./lib/.tsbuildinfo` doesn't exist, it'll be generated.
But if it does, `tsc` will try to use that file to incrementally type-check and
update our output files.
These `.tsbuildinfo` files can be safely deleted and don't have any impact on our
code at runtime - they're purely used to make compilations faster.
We can also name them anything that we want, and place them anywhere we want using
the `--tsBuildInfoFile` flag.
```json tsconfig
// front-end.tsconfig.json
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./buildcache/front-end",
"outDir": "./lib"
},
"include": ["./src"]
}
t t
Composite projects
Part of the intent with composite projects ( tsconfig.json s with
composite set to true ) is that references between different projects
can be built incrementally. As such, composite projects will always
produce .tsbuildinfo files.
outFile
When outFile is used, the build information file's name will be based
on the output file's name. As an example, if our output JavaScript file
is ./output/foo.js , then under the --incremental flag, TypeScript will
generate the file ./output/foo.tsbuildinfo . As above, this can be
controlled with the --tsBuildInfoFile flag.
Higher order type inference
from generic functions
TypeScript 3.4 can now produce generic function types when inference
from other generic functions produces free type variables for
inferences. This means many function composition patterns now work
better in 3.4.
To get more specific, let's build up some motivation and consider the
following compose function:
function compose<A, B, C>(f: (arg: A) => B, g: (arg: B) => C): (arg: A) => C {
return (x) => g(f(x));
}
and then g .
When calling this function, TypeScript will try to figure out the types
of A , B , and C through a process called type argument inference.
This inference process usually works pretty well:
interface Person {
name: string;
age: number;
}
interface Box<T> {
value: T;
}
makeBoxedArray("hello!").value[0].toUpperCase();
// ~~~~~~~~~~~
// error: Property 'toUpperCase' does not exist on type '{}'.
In older versions, TypeScript would infer the empty object type ( {} )
when inferring from other type variables like T and U .
Notice that T has been propagated from makeArray into the resulting
type's type parameter list. This means that genericity from compose 's
arguments has been preserved and our makeBoxedArray sample will just
work!
interface Box<T> {
value: T;
}
For more details, you can read more at the original change.
Improvements for
ReadonlyArray and readonly
tuples
TypeScript 3.4 makes it a little bit easier to use read-only array-like
types.
readonly tuples
TypeScript 3.4 also introduces new support for readonly tuples. We
can prefix any tuple type with the readonly keyword to make it a
readonly tuple, much like we now can with array shorthand syntax. As
you might expect, unlike ordinary tuples whose slots could be written
to, readonly tuples only permit reading from those positions.
The same way that ordinary tuples are types that extend from Array
type Boxify<T> = {
[K in keyof T]: Box<T[K]>;
};
// { a: Box<string>, b: Box<number> }
type A = Boxify<{ a: string; b: number }>;
// Array<Box<number>>
type B = Boxify<number[]>;
// [Box<string>, Box<number>]
type C = Boxify<[string, boolean]>;
// lib.d.ts
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// number[]
type B = Readonly<number[]>;
// [string, boolean]
type C = Readonly<[string, boolean]>;
counterparts.
// How code acts now *with* TypeScript 3.4
// readonly number[]
type B = Readonly<number[]>;
Similarly, you could write a utility type like Writable mapped type that
strips away readonly -ness, and that would convert readonly array
containers back to their mutable equivalents.
type Writable<T> = {
-readonly [K in keyof T]: T[K];
};
// { a: string, b: number }
type A = Writable<{
readonly a: string;
readonly b: number;
}>;
// number[]
type B = Writable<readonly number[]>;
// [string, boolean]
type C = Writable<readonly [string, boolean]>;
Caveats
Despite its appearance, the readonly type modifier can only be used
for syntax on array types and tuple types. It is not a general-
purpose type operator.
let err1: readonly Set<number>; // error!
let err2: readonly Array<boolean>; // error!
// Type '"hello"'
let x = "hello" as const;
Outside of .tsx files, the angle bracket assertion syntax can also be
used.
// Type '"hello"'
let x = <const>"hello";
This feature means that types that would otherwise be used just to
hint immutability to the compiler can often be omitted.
// Works with no types referenced or declared.
// We only needed a single const assertion.
function getShapes() {
let result = [
{ kind: "circle", radius: 100 },
{ kind: "square", sideLength: 50 },
] as const;
return result;
}
This can even be used to enable enum -like patterns in plain JavaScript
code if you choose not to use TypeScript's enum construct.
export default {
red: "RED",
blue: "BLUE",
green: "GREEN",
} as const;
Caveats
One thing to note is that const assertions can only be applied
immediately on simple literal expressions.
// Works!
let b = Math.random() < 0.5 ? (0 as const) : (1 as const);
let foo = {
name: "foo",
contents: arr,
} as const;
foo.contents.push(5); // ...works!
For more details, you can check out the respective pull request.
Type-checking for globalThis
TypeScript 3.4 introduces support for type-checking ECMAScript's new
globalThis - a global variable that, well, refers to the global scope.
Unlike the above solutions, globalThis provides a standard way for
accessing the global scope which can be used across different
environments.
// in a global file:
Note that global variables declared with let and const don't show up
on globalThis .
type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruit
type ColorConsumer = (color: Color) => string; // consumes and describes the colors
or a string .
type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruit
type ColorConsumer = (color: Color) => string; // consumes and describes the colors
Color is really the same as ("apple" | "orange") & ("red" | "orange") which is
equivalent to ("apple" & "red") | ("apple" & "orange") | ("orange" & "red") |
Caveats
This new behavior only kicks in when at most one type in the union has
multiple overloads, and at most one type in the union has a generic
signature. That means methods on number[] | string[] like map (which is
generic) still won't be callable.
On the other hand, methods like forEach will now be callable, but
under noImplicitAny there may be some issues.
interface Dog {
kind: "dog";
dogProp: any;
}
interface Cat {
kind: "cat";
catProp: any;
}
catOrDogArray.forEach((animal) => {
// ~~~~~~ error!
// Parameter 'animal' implicitly has an 'any' type.
});
interface Dog {
kind: "dog";
dogProp: any;
}
interface Cat {
kind: "cat";
catProp: any;
}
strictBindCallApply
TypeScript 3.2 introduces a new --strictBindCallApply compiler option
(in the --strict family of options) with which the bind , call , and
apply methods on function objects are strongly typed and strictly
checked.
Caveats
Since the stricter checks may uncover previously unreported errors, this
is a breaking change in --strict mode.
Additionally, another caveat of this new functionality is that due to
certain limitations, bind , call , and apply can't yet fully model
generic functions or functions that have overloads. When using these
methods on a generic function, type parameters will be substituted with
the empty object type ( {} ), and when used on a function with
overloads, only the last overload will ever be modeled.
Generic spread expressions in
object literals
In TypeScript 3.2, object literals now allow generic spread expressions
which now produce intersection types, similar to the Object.assign
helper types from lib.d.ts , and using the generic type in question as
well as the names of the other bindings in the destructuring pattern.
fibonacci(10000n);
While you might imagine close interaction between number and bigint ,
the two are separate domains.
We'd like to extend a huge thanks to Caleb Sander for all the work on
this feature. We're grateful for the contribution, and we're sure our
users are too!
Caveats
As we mentioned, BigInt support is only available for the esnext
This feature can be extremely useful for bigger organizations, or projects with
lots of distributed dependencies.
```js
// @ts-check
obj.x.toLowercase();
// ~~~~~~~~~~~
// error:
// Property 'toLowercase' does not exist on type 'string'.
// Did you mean 'toLowerCase'?
obj.x = "world";
// ~
// error:
// Cannot assign to 'x' because it is a read-only property.
TypeScript 3.1
Promise<number>] .
Properties declarations on
functions
TypeScript 3.1 brings the ability to define properties on function
declarations and const -declared functons, simply by assigning to
properties on these functions in the same scope. This allows us to
write canonical JavaScript code without resorting to namespace hacks.
For example:
function readImage(path: string, callback: (err: any, image: Image) => void) {
// ...
}
FooComponent.defaultProps = {
name: "(anonymous)",
};
[1]
[1] More specifically, homomorphic mapped types like in the above form.
Version selection with
typesVersions
Feedback from our community, as well as our own experience, has
shown us that leveraging the newest TypeScript features while also
accomodating users on the older versions are difficult. TypeScript
introduces a new feature called typesVersions to help accomodate these
scenarios.
The way that TypeScript decides on whether a version of the compiler & language
matches is by using Node's [semver ranges](https://github.com/npm/node-
semver#ranges).
`typesVersions` can support multiple fields where each field name is specified by
the range to match on.
```json tsconfig
{
"name": "package-name",
"version": "1.0",
"types": "./index.d.ts",
"typesVersions": {
">=3.2": { "*": ["ts3.2/*"] },
">=3.1": { "*": ["ts3.1/*"] }
}
}
## TypeScript 3.0
TypeScript 3.0 adds support to multiple new capabilities to interact with function
parameter lists as tuple types.
TypeScript 3.0 adds support for:
When a rest parameter has a tuple type, the tuple type is expanded into a sequence
of discrete parameters.
For example the following two declarations are equivalent:
```ts
declare function foo(...args: [number, string, boolean]): void;
Example
modifier on its type and all elements to the right of it also have ?
modifiers.
When tuple types are inferred for rest parameters, optional parameters
in the source become optional tuple elements in the inferred type.
boolean?] is 1 | 2 | 3 .
elements.
Example
The type of the length property of a tuple type with a rest element
is number .
New unknown top type
TypeScript 3.0 introduces a new top type unknown . unknown is the
type-safe counterpart of any . Anything is assignable to unknown , but
unknown isn't assignable to anything but itself and any without a type
assertion or a control flow based narrowing. Likewise, no operations
are permitted on an unknown without first asserting or narrowing to a
more specific type.
Example
// In an intersection everything absorbs unknown
// keyof unknown
function f25() {
let x: unknown;
let y = x;
}
class C1 {
a: string; // Error
b: unknown;
c: any;
}
Support for defaultProps in
JSX
TypeScript 2.9 and earlier didn’t leverage React defaultProps
TypeScript 3.0 adds supports a new type alias in the JSX namespace
called LibraryManagedAttributes . This helper type defines a transformation
on the component's Props type, before using to check a JSX
expression targeting it; thus allowing customization like: how conflicts
between provided props and inferred props are handled, how
inferences are mapped, how optionality is handled, and how inferences
from differing places should be combined.
Caveats
l t t
Explicit types on defaultProps
Changes to @types/React
Built-in lib files are referenced in the same fashion as the "lib"
For declaration file authors who rely on built-in types, e.g. DOM APIs
or built-in JS run-time constructors like Symbol or Iterable , triple-
slash-reference lib directives are the recommended. Previously these
.d.ts files had to add forward/duplicate declarations of such types.
Example
"foo".padStart(4);
TypeScript 2.9
Changes include:
number | symbol .
A mapped type { [P in K]: XXX } permits any K assignable to
string | number | symbol .
In a for...in statement for an object of a generic type T , the
inferred type of the iteration variable was previously keyof T but
is now Extract<keyof T, string> . (In other words, the subset of
keyof T that includes only string-like values.)
Where:
String-like properties of an object type are those declared using
an identifier, a string literal, or a computed property name of a
string literal type.
Number-like properties of an object type are those declared
using a numeric literal or computed property name of a numeric
literal type.
Symbol-like properties of an object type are those declared using
a computed property name of a unique symbol type.
Example
const c = "c";
const d = 10;
const e = Symbol();
const enum E1 {
A,
B,
C,
}
const enum E2 {
A = "A",
B = "B",
C = "C",
}
type Foo = {
a: string; // String-like name
5: string; // Number-like name
[c]: string; // String-like name
[d]: string; // Number-like name
[e]: string; // Symbol-like name
[E1.A]: string; // Number-like name
[E2.A]: string; // String-like name
};
const enumToStringMap = {
[Enum.A]: "Name A",
[Enum.B]: "Name B",
[Enum.C]: "Name C",
};
const symbolToNumberMap = {
[sym1]: 1,
[sym2]: 2,
[sym3]: 3,
};
Example
function useKey<T, K extends keyof T>(o: T, k: K) {
var name: string = k; // Error: keyof T is not assignable to string
}
Recommendations
If your functions are only able to handle string named property
keys, use Extract<keyof T, string> in the declaration:
If your functions are open to handling all property keys, then the
changes should be done down-stream:
Example
class GenericComponent<P> extends React.Component<P> {
internalProp: P;
}
Example
declare function styledComponent<Props>(
strs: TemplateStringsArray
): Component<Props>;
interface MyProps {
name: string;
age: number;
}
styledComponent<MyProps>`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
// inference fails because 'number' and 'string' are both candidates that conflict
let a = tag<string | number>`${100} ${"hello"}`;
import types
Modules can import types declared in other modules. But non-module
global scripts cannot access types declared in modules. Enter import
types.
Example
Given a declaration of a class Pet in a module file:
// module.d.ts
// global-script.ts
/**
* @param p { import("./module").Pet }
*/
function walk(p) {
console.log(`Walking ${p.name}...`);
}
Relaxing declaration emit
visiblity rules
With import types available, many of the visibility errors reported
during declaration file generation can be handled by the compiler
without the need to change the input.
For instance:
With TypeScript 2.9, no errors are reported, and now the generated
file looks like:
Example
Assuming that __dirname is always available on import.meta , the
declaration would be done through reopening ImportMeta interface:
// node.d.ts
interface ImportMeta {
__dirname: string;
}
Example
// settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
// a.ts
Starting TypeScript 2.9 errors are displayed under `--pretty` by default if the
output device is applicable for colorful text.
TypeScript will check if the output steam has [`isTty`]
(https://nodejs.org/api/tty.html) property set.
Use `--pretty false` on the command line or set `"pretty": false` in your
`tsconfig.json` to disable `--pretty` output.
## New `--declarationMap`
## TypeScript 2.8
## Conditional Types
TypeScript 2.8 introduces _conditional types_ which add the ability to express non-
uniform type mappings.
A conditional type selects one of two possible types based on a condition expressed
as a type relationship test:
```ts
T extends U ? X : Y
First, given types T' and U' that are instantiations of T and
U where all occurrences of type parameters are replaced with
any , if T' is not assignable to U' , the conditional type is
resolved to Y . Intuitively, if the most permissive instantiation of
T is not assignable to the most permissive instantiation of U ,
we know that no instantiation will be and we can just resolve to
Y .
Next, for each type variable introduced by an infer (more later)
declaration within U collect a set of candidate types by inferring
from T to U (using the same inference algorithm as type
inference for generic functions). For a given infer type variable
V , if any candidates were inferred from co-variant positions,
the type inferred for V is a union of those candidates.
Otherwise, if any candidates were inferred from contra-variant
positions, the type inferred for V is an intersection of those
candidates. Otherwise, the type inferred for V is never .
Then, given a type T'' that is an instantiation of T where all
infer type variables are replaced with the types inferred in the
previous step, if T'' is definitely assignable to U , the
conditional type is resolved to X . The definitely assignable
relation is the same as the regular assignable relation, except that
type variable constraints are not considered. Intuitively, when a
type is definitely assignable to another type, we know that it will
be assignable for all instantiations of those types.
Otherwise, the condition depends on one or more type variables
and the conditional type is deferred.
Example
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
| (C extends U ? X : Y) .
Example
type T10 = TypeName<string | (() => void)>; // "string" | "function"
type T12 = TypeName<string | string[] | undefined>; // "string" | "object" |
"undefined"
type T11 = TypeName<string[] | number[]>; // "object"
within X ).
Example
type BoxedValue<T> = { value: T };
type BoxedArray<T> = { array: T[] };
type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
Notice that T has the additional constraint any[] within the true
branch of Boxed<T> and it is therefore possible to refer to the
element type of the array as T[number] . Also, notice how the
conditional type is distributed over the union type in the last example.
type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T32 = Diff<string | number | (() => void), Function>; // string | number
type T33 = Filter<string | number | (() => void), Function>; // () => void
type NonNullable<T> = Diff<T, null | undefined>; // Remove null and undefined from
T
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
interface Part {
id: number;
name: string;
subparts: Part[];
updatePart(newName: string): void;
}
Example
type ElementType<T> = T extends any[] ? ElementType<T[number]> : T; // Error
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string &
number
When inferring from a type with multiple call signatures (such as the
type of an overloaded function), inferences are made from the last
signature (which, presumably, is the most permissive catch-all case). It
is not possible to perform overload resolution based on a list of
argument types.
type ReturnType<T extends (...args: any[]) => infer R> = R; // Error, not supported
However, much the same effect can be obtained by erasing the type
variables in the constraint and instead specifying a conditional type:
l
Example
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T02 = Exclude<string | number | (() => void), Function>; // string | number
type T03 = Extract<string | number | (() => void), Function>; // () => void
class C {
x = 0;
y = 0;
}
T, K>> .
Improved control over
mapped type modifiers
Mapped types support adding a readonly or ? modifier to a mapped
property, but they did not provide support the ability to remove
modifiers. This matters in homomorphic mapped types which by default
preserve the modifiers of the underlying type.
TypeScript 2.8 adds the ability for a mapped type to either add or
remove a particular modifier. Specifically, a readonly or ? property
modifier in a mapped type can now be prefixed with either + or -
Example
type MutableRequired<T> = { -readonly [P in keyof T]-?: T[P] }; // Remove readonly
and ?
type ReadonlyPartial<T> = { +readonly [P in keyof T]+?: T[P] }; // Add readonly and
?
Using this ability, lib.d.ts now has a new Required<T> type. This type
strips ? modifiers from all properties of T , thus making all
properties required.
Example
type Required<T> = { [P in keyof T]-?: T[P] };
Example
type Foo = { a?: string }; // Same as { a?: string | undefined }
type Bar = Required<Foo>; // Same as { a: string }
Improved keyof with
intersection types
With TypeScript 2.8 keyof applied to an intersection type is
transformed to a union of keyof applied to each intersection
constituent. In other words, types of the form keyof (A & B) are
transformed to be keyof A | keyof B . This change should address
inconsistencies with inference from keyof expressions.
Example
type A = { a: string };
type B = { b: string };
var C = (function () {
function C(n) {
this.p = n;
}
return C;
})();
C.staticProperty = 1;
f lt d d l t
Defaulted declarations
"Defaulted declarations" allow initializers that reference the declared
name in the left side of a logical or:
my = window.my || {};
my.app = my.app || {};
Prototype assignment
You can assign an object literal directly to the prototype property.
Individual prototype assignments still work too:
Example
/** @jsx dom */
import { dom } from "./renderer";
<h></h>;
Generates:
Constant-named properties
TypeScript 2.7 adds support for declaring const-named properties on
types including ECMAScript symbols.
Example
// Lib
export const SERIALIZE = Symbol("serialize-method-key");
// consumer
Example
const Foo = "Foo";
const Bar = "Bar";
let x = {
[Foo]: 100,
[Bar]: "hello",
};
unique symbol
To enable treating symbols as unique literals a new type unique symbol
Example
// Works
declare const Foo: unique symbol;
// Also works.
class C {
static readonly StaticSymbol: unique symbol = Symbol();
}
Because each unique symbol has a completely separate identity, no two
unique symbol types are assignable or comparable to each other.
Example
const Foo = Symbol();
const Bar = Symbol();
class C {
foo: number;
bar = "hello";
baz: boolean;
// ~~~
// Error! Property 'baz' has no initializer and is not definitely assigned in
the
// constructor.
constructor() {
this.foo = 42;
}
}
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
For example:
let x: number;
initialize();
console.log(x + x);
// ~ ~
// Error! Variable 'x' is used before being assigned.
function initialize() {
x = 10;
}
// No error!
console.log(x + x);
function initialize() {
x = 10;
}
// No error!
console.log(x! + x!);
function initialize() {
x = 10;
Note that this is a breaking change for some code. If you need to
resort to the original behavior in which tuples only enforce a minimum
length, you can use a similar declaration that does not explicitly define
a length property, falling back to number .
Consider:
const obj = test ? { text: "hello" } : {}; // { text: string } | { text?: undefined
}
const s = obj.text; // string | undefined
Previously type {} was inferred for obj and the second line
subsequently caused an error because obj would appear to have no
properties. That obviously wasn't ideal.
Example
// let obj: { a: number, b: number } |
// { a: string, b?: undefined } |
// { a?: undefined, b?: undefined }
let obj = [{ a: 1, b: 2 }, { a: "abc" }, {}][0];
obj.a; // string | number | undefined
obj.b; // number | undefined
Multiple object literal type inferences for the same type parameter are
similarly collapsed into a single normalized union type:
declare function f<T>(...items: T[]): T;
// let obj: { a: number, b: number } |
// { a: string, b?: undefined } |
// { a?: undefined, b?: undefined }
let obj = f({ a: 1, b: 2 }, { a: "abc" }, {});
obj.a; // string | number | undefined
obj.b; // number | undefined
Improved handling of
structurally identical classes
and instanceof expressions
TypeScript 2.7 improves the handling of structurally identical classes in
union types and instanceof expressions:
This means that union types and instanceof properly distinguish between
structurally identical classes.
Example:
class A {}
class B extends A {}
class C extends A {}
class D extends A {
c: string;
}
class E extends D {}
let a1 = [new A(), new B(), new C(), new D(), new E()]; // A[]
let a2 = [new B(), new C(), new D(), new E()]; // (B | C | D)[] (previously B[])
function f1(x: B | C | D) {
if (x instanceof B) {
x; // B (previously B | D)
} else if (x instanceof C) {
x; // C
} else {
x; // D (previously never)
}
}
Type guards inferred from
in operator
The in operator now acts as a narrowing expression for types.
Example
interface A {
a: number;
}
interface B {
b: string;
}
function foo(x: A | B) {
if ("a" in x) {
return x.a;
}
return x.b;
}
Support for import d from
"cjs" form CommonJS
modules with --esModuleInterop
TypeScript 2.7 updates CommonJS/AMD/UMD module emit to synthesize
namespace records based on the presence of an __esModule indicator
under --esModuleInterop . The change brings the generated output from
TypeScript closer to that generated by Babel.
foo = require("foo") . Things are simple here, but they don't work
out if the primary object being imported is a primitive or a class
or a function. ECMAScript spec stipulates that a namespace record
is a plain object, and that a namespace import ( foo in the
example above) is not callable, though allowed by TypeScript
Example
With --esModuleInterop two new helpers are generated __importStar and
__importDefault for import * and import default respectively. For
instance input like:
Will generate:
"use strict";
var __importStar =
(this && this.__importStar) ||
function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
exports.__esModule = true;
var foo = __importStar(require("foo"));
var bar_1 = __importDefault(require("bar"));
Numeric separators
TypeScript 2.7 brings support for ES Numeric Separators. Numeric
literals can now be separated into segments using _ .
Example
Another way to describe the example is that the type (x: T) => void is
bivariant (i.e. covariant or contravariant) for T in default type
checking mode, but contravariant for T in strict function types
mode.
Example
interface Comparer<T> {
compare: (a: T, b: T) => number;
}
By the way, note that whereas some languages (e.g. C# and Scala)
require variance annotations ( out / in or + / - ), variance emerges
naturally from the actual use of a type parameter within a generic type
due to TypeScript's structural type system.
Note:
Under --strictFunctionTypes the first assignment is still permitted if
compare was declared as a method. Effectively, T is bivariant in
Comparer<T> because it is used only in method parameter positions.
interface Comparer<T> {
compare(a: T, b: T): number;
}
as shown below.
render() {
return (
<div>
Some text.
<h2>A heading</h2>
More text.
</div>
);
}
render() {
return (
<>
Some text.
<h2>A heading</h2>
More text.
</>
);
}
Under --jsx preserve , the new syntax is left untouched for TypeScript
emit. Otherwise, for --jsx react , <>...</> is compiled to
React.createElement(React.Fragment, null, ...) , where React.createElement
Please refer to the React blog for more details on fragments and the
new syntax.
Cache tagged template
objects in modules
TypeScript 2.6 fixes the tagged string template emit to align better
with the ECMAScript spec. As per the ECMAScript spec, every time a
template tag is evaluated, the same template strings object (the same
TemplateStringsArray ) should be passed as the first argument. Before
TypeScript 2.6, the generated output was a completely new template
object each time. Though the string contents are the same, this emit
affects libraries that use the identity of the string for cache
invalidation purposes, e.g. lit-html.
Example
export function id(x: TemplateStringsArray) {
return x;
}
function id(x) {
return x;
}
var _a;
function templateObjectFactory() {
return id(
_a || (_a = __makeTemplateObject(["hello world"], ["hello world"]))
);
}
Example
Error messages in Russian:
c:\ts>tsc --v
Version 2.6.0-dev.20171003
../test/a.ts(1,5): error TS2322: Тип ""string"" не может быть назначен для типа
"number".
オプション:
-h, --help このメッセージを表⽰します。
--all コンパイラ オプションをすべて表⽰します。
-v, --version コンパイラのバージョンを表⽰します。
--init TypeScript プロジェクトを初期化して、
tsconfig.jsonファイルを作成します。
-p ファイルまたはディレクトリ, --project ファイルまたはディレクトリ 構成ファイル
か、'tsconfig.json' を含むフォルダーにパスが指定されたプロジェクトをコ
ンパイルします。
--pretty ⾊とコンテキストを使⽤してエラーとメッセージに
スタイルを適⽤します (試験的)。
-w, --watch ⼊⼒ファイルを監視します。
-t バージョン, --target バージョン ECMAScript のターゲット バージョンを
指定します: 'ES3' (既定)、'ES5'、'ES2015'、'ES2016'、'ES2017'、'ES
NEXT'。
-m 種類, --module 種類 モジュール コード⽣成を指定します:
'none'、'commonjs'、'amd'、'system'、'umd'、'es2015'、'ESNext'。
--lib コンパイルに含めるライブラリ ファイルを指定し
ます:
'es5' 'es6' 'es2015' 'es7' 'es2016'
'es2017' 'esnext' 'dom' 'dom.iterable' 'webworker' 'scripthost' 'es201
5.core' 'es2015.collection' 'es2015.generator' 'es2015.iterable' 'es2015.promise'
'es2015.proxy' 'es2015.reflect' 'es2015.symbol' 'es2015.symbol.wellkno
wn' 'es2016.array.include' 'es2017.object' 'es2017.sharedmemory' 'es2017.string'
'es2017.intl' 'esnext.asynciterable'
--allowJs javascript ファイルのコンパイルを許可しま
す。
--jsx 種類 JSX コード⽣成を指定します:
'preserve'、'react-native'、'react'。
-d, --declaration 対応する '.d.ts' ファイルを⽣成します。
--sourceMap 対応する '.map' ファイルを⽣成します。
--outFile ファイル 出⼒を連結して 1 つのファイルを⽣成します。
--outDir ディレクトリ ディレクトリへ出⼒構造をリダイレクトしま
す。
--removeComments コメントを出⼒しないでください。
--noEmit 出⼒しないでください。
--strict strict 型チェックのオプションをすべて有効にし
ます。
--noImplicitAny 暗黙的な 'any' 型を含む式と宣⾔に関するエラー
を発⽣させます。
--strictNullChecks 厳格な null チェックを有効にします。
--noImplicitThis 暗黙的な 'any' 型を持つ 'this' 式でエラーが
発⽣します。
--alwaysStrict 厳格モードで解析してソース ファイルごとに
"use strict" を⽣成します。
--noUnusedLocals 使⽤されていないローカルに関するエラーを報告し
ます。
--noUnusedParameters 使⽤されていないパラメーターに関するエラーを報
告します。
--noImplicitReturns 関数の⼀部のコード パスが値を返さない場合にエ
ラーを報告します。
--noFallthroughCasesInSwitch switch ステートメントに case のフォールス
ルーがある場合にエラーを報告します。
--types コンパイルに含む型宣⾔ファイル。
@<ファイル>
Suppress errors in .ts files
using '// @ts-ignore'
comments
TypeScript 2.6 support suppressing errors in .js files using // @ts-
Example
if (false) {
// @ts-ignore: Unreachable code error
console.log("hello");
}
Please note that this comment only suppresses the error reporting, and
we recommend you use this comments very sparingly.
Faster tsc --watch
TypeScript 2.6 brings a faster --watch implementation. The new
version optimizes code generation and checking for code bases using ES
modules. Changes detected in a module file will result in only
regenerating the changed module, and files that depend on it, instead
of the whole project. Projects with large number of files should reap
the most benefit from this change.
Example
Bellow both n and m will be marked as unused, because their values
are never read. Previously TypeScript would only check whether their
values were referenced.
class C {
private m: number;
constructor() {
this.m = 0;
}
}
Also functions that are only called within their own bodies are
considered unused.
Example
function f() {
f(); // Error: 'f' is declared but its value is never read
}
TypeScript 2.5
clauses. For example, when using JSON.parse you may need to wrap calls
to the function with a try / catch , but you may not end up using the
SyntaxError that gets thrown when input is erroneous.
As a nice bonus, this can also reduce the memory and runtime
footprint of the compiler and language service by avoiding loading
.d.ts files from duplicate packages.
The --preserveSymlinks
compiler flag
TypeScript 2.5 brings the preserveSymlinks flag, which parallels the
behavior of the --preserve-symlinks flag in Node.js. This flag also
exhibits the opposite behavior to Webpack's resolve.symlinks option
(i.e. setting TypeScript's preserveSymlinks to true parallels setting
Webpack's resolve.symlinks to false , and vice-versa).
This means that you can conditionally and lazily import other modules
and libraries. For example, here's an async function that only imports
a utility library when it's needed:
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {
return (a) => a.map(f);
}
In TypeScript 2.4, the function on the right side implicitly gains type
parameters, and y is inferred to have the type of that type-
parameter.
function f(a: A, b: B) {
a = b; // Error
b = a; // Ok
}
Strict contravariance for
callback parameters
TypeScript has always compared parameters in a bivariant way. There
are a number of reasons for this, but by-and-large this was not been
a huge issue for our users until we saw some of the adverse effects it
had with Promise s and Observable s.
interface Mappable<T> {
map<U>(f: (x: T) => U): Mappable<U>;
}
a = b;
b = a;
Prior to TypeScript 2.4, this example would succeed. When relating the
types of map , TypeScript would bidirectionally relate their parameters
(i.e. the type of f ). When relating each f , TypeScript would also
bidirectionally relate the type of those parameters.
When relating the type of map in TS 2.4, the language will check
whether each parameter is a callback type, and if so, it will ensure
that those parameters are checked in a contravariant manner with
respect to the current relation.
In other words, TypeScript now catches the above bug, which may be a
breaking change for some users, but will largely be helpful.
Weak Type Detection
TypeScript 2.4 introduces the concept of "weak types". Any type that
contains nothing but a set of all-optional properties is considered to
be weak. For example, this Options type is a weak type:
interface Options {
data?: string;
timeout?: number;
maxRetries?: number;
}
const opts = {
payload: "hello world!",
retryOnFail: true,
};
// Error!
sendMessage(opts);
// No overlap between the type of 'opts' and 'Options' itself.
// Maybe we meant to use 'data'/'maxRetries' instead of 'payload'/'retryOnFail'.
Since this is a breaking change, you may need to know about the
workarounds which are the same as those for strict object literal
checks:
{} ).
3. Use a type assertion (i.e. opts as Options ).
TypeScript 2.3
Iterators
ES2015 introduced Iterator , which is an object that exposes three
methods, next , return , and throw , as per the following interface:
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
The Iterator protocol also defines the target of some of the ES2015
features like for..of and spread operator and the array rest in
destructuring assignmnets.
Generators
ES2015 also introduced "Generators", which are functions that can be
used to yield partial computation results via the Iterator interface and
the yield keyword. Generators can also internally delegate calls to
another iterable through yield * . For example:
function* f() {
yield 1;
yield* [2, 3];
}
New --downlevelIteration
TypeScript 2.3 adds full support for generators and the Iterator
protocol for ES3 and ES5 targets with --downlevelIteration flag.
With --downlevelIteration , the compiler uses new type check and emit
behavior that attempts to call a [Symbol.iterator]() method on the
iterated object if it is found, and creates a synthetic array iterator
over the object if it is not.
Async iterators
The Async Iteration introduces an AsyncIterator , which is similar to
Iterator . The difference lies in the fact that the next , return , and
throw methods of an AsyncIterator return a Promise for the iteration
result, rather than the result itself. This allows the caller to enlist in
an asynchronous notification for the time at which the AsyncIterator has
advanced to the point of yielding a value. An AsyncIterator has the
following shape:
interface AsyncIterator<T> {
next(value?: any): Promise<IteratorResult<T>>;
return?(value?: any): Promise<IteratorResult<T>>;
throw?(e?: any): Promise<IteratorResult<T>>;
}
Async Generators
The Async Iteration proposal introduces "Async Generators", which are
async functions that also can be used to yield partial computation
results. Async Generators can also delegate calls via yield* to either
an iterable or async iterable:
async function* g() {
yield 1;
await sleep(100);
yield* [2, 3];
yield* (async function* () {
await sleep(100);
yield 4;
})();
}
Caveats
Keep in mind that our support for async iterators relies on
support for Symbol.asyncIterator to exist at runtime. You may need
to polyfill Symbol.asyncIterator , which for simple purposes can be as
simple as: (Symbol as any).asyncIterator = Symbol.asyncIterator ||
Symbol.from("Symbol.asyncIterator");
You also need to include esnext in your --lib option, to get the
AsyncIterator declaration if you do not already have it.
Finally, if your target is ES5 or ES3, you'll also need to set the
--downlevelIterators flag.
Generic parameter defaults
TypeScript 2.3 adds support for declaring defaults for generic type
parameters.
Example
Consider a function that creates a new HTMLElement , calling it with no
arguments generates a Div ; you can optionally pass a list of children
as well. Previously you would have to define it as:
--strictNullChecks
--noImplicitAny
--noImplicitThis
--alwaysStrict
In exact terms, the --strict option sets the default value for the
compiler options listed above. This means it is still possible to
individually control the options. For example,
section. Thus, new projects started with tsc --init will by default have
the highest level of type safety enabled.
Enhanced --init output
Along with setting --strict on by default, tsc --init has an enhanced
output. Default tsconfig.json files generated by tsc --init now include
a set of the common compiler options along with their descriptions
commented out. Just un-comment the configuration you like to set to
get the desired behavior; we hope the new output simplifies the setting
up new projects and keeps configuration files readable as projects
grow.
Errors in .js files with --
checkJs
By default the TypeScript compiler does not report any errors in .js
files including using --allowJs . With TypeScript 2.3 type-checking
errors can also be reported in .js files with --checkJs .
.js files are still checked to ensure that they only include standard
ECMAScript features; type annotations are only allowed in .ts files and
are flagged as errors in .js files. JSDoc comments can be used to
add some type information to your JavaScript code, see JSDoc
Support documentation for more details about the supported JSDoc
constructs.
The constructor of a mixin class (if any) must have a single rest
parameter of type any[] and must use the spread operator to
pass those parameters as arguments in a super(...args) call.
class Point {
constructor(public x: number, public y: number) {}
}
class Person {
constructor(public name: string) {}
}
interface Point {
x: number;
y: number;
}
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
Support for new.target
The new.target meta-property is new syntax introduced in ES2015.
When an instance of a constructor is created via new , the value of
new.target is set to be a reference to the constructor function initially
used to allocate the instance. If a function is called rather than
constructed via new , new.target is set to undefined .
Example
class CustomError extends Error {
constructor(message?: string) {
super(message); // 'Error' breaks prototype chain here
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
}
}
function f() {
if (new.target) {
/* called via 'new' */
}
}
function f() {
var _newTarget = this && this instanceof f ? this.constructor : void 0;
if (_newTarget) {
/* called via 'new' */
}
}
Better checking for
null / undefined in operands
of expressions
TypeScript 2.2 improves checking of nullable operands in expressions.
Specifically, these are now flagged as errors:
notation, but were not allowed to use the . . Starting with TypeScript
2.2 using either should be allowed.
interface StringMap<T> {
[x: string]: T;
}
map["prop1"] = 1;
map.prop2 = 2;
notation.
Support for spread operator
on JSX element children
TypeScript 2.2 adds support for using spread on a JSX element
children. Please see facebook/jsx#57 for more details.
Example
function Todo(prop: { key: number; todo: string }) {
return <div>{prop.key.toString() + prop.todo}</div>;
}
let x: TodoListProps;
will persevere the JSX syntax in the output file, but give it a .js
extension.
TypeScript 2.1
Example
interface Person {
name: string;
age: number;
location: string;
}
The dual of this is indexed access types, also called lookup types.
Syntactically, they look exactly like an element access, but are written
as types:
Example
interface Person {
name: string;
age: number;
location: string;
}
interface PartialPerson {
name?: string;
age?: number;
location?: string;
}
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Same property names, but make the value a promise instead of a concrete one
type Deferred<T> = {
[P in keyof T]: Promise<T[P]>;
};
We're also including two other utility types as well: Record and Pick .
You can also override existing properties and add new ones:
Object rests are the dual of object spreads, in that they can extract
any extra properties that don't get picked up when destructuring an
element:
let obj = { x: 1, y: 1, z: 1 };
let { z, ...obj1 } = obj;
obj1; // {x: number, y: number};
Downlevel Async Functions
This feature was supported before TypeScript 2.1, but only when
targeting ES6/ES2015. TypeScript 2.1 brings the capability to ES3 and
ES5 run-times, meaning you'll be free to take advantage of it no
matter what environment you're using.
Example
tsconfig.json
```ts
function delay(milliseconds: number) {
return new Promise<void>((resolve) => {
setTimeout(resolve, milliseconds);
});
}
console.log("World!");
}
dramaticWelcome();
Compiling and running the output should result in the correct behavior
on an ES3/ES5 engine.
Support for external helpers
library ( tslib )
TypeScript injects a handful of helper functions such as __extends for
inheritance, __assign for spread operator in object literals and JSX
elements, and __awaiter for async functions.
The two options left more to be desired; bundling the helpers in every
file was a pain point for customers trying to keep their package size
small. And not including helpers, meant customers had to maintain their
own helpers library.
TypeScript 2.1 allows for including these files in your project once in a
separate module, and the compiler will emit imports to them as
needed.
So given the following input, the resulting .js file will include an
import to tslib and use the __assign helper from it instead of
inlining it.
However, a lot of the time, you might just want to import an existing
module that may not have its own .d.ts file. Previously this was an
error. Starting with TypeScript 2.1 this is now much easier.
Example
Using target --target ES2016 will instruct the compiler not to transform
ES2016-specific features, e.g. ** operator.
Example
let x;
// After that last assignment, TypeScript 2.1 knows that 'x' has type '() =>
number'.
let y = x();
// Thanks to that, it will now tell you that you can't add a number to a function!
console.log(x + y);
// ~~~~~
// Error! Operator '+' cannot be applied to types '() => number' and 'number'.
The same sort of tracking is now also done for empty arrays.
A variable declared with no type annotation and an initial value of []
function f1() {
let x = [];
x.push(5);
x[1] = "hello";
x.unshift(true);
return x; // (string | number | boolean)[]
}
function f2() {
let x = null;
if (cond()) {
x = [];
while (cond()) {
x.push("hello");
}
}
return x; // string[] | null
}
Implicit any errors
One great benefit of this is that you'll see way fewer implicit any
errors when running with --noImplicitAny . Implicit any errors are only
reported when the compiler is unable to know the type of a variable
without a type annotation.
Example
function f3() {
let x = []; // Error: Variable 'x' implicitly has type 'any[]' in some locations
where its type cannot be determined.
x.push(5);
function g() {
x; // Error: Variable 'x' implicitly has an 'any[]' type.
}
}
Better inference for literal
types
String, numeric and boolean literal types (e.g. "abc" , 1 , and true )
were previously inferred only in the presence of an explicit type
annotation. Starting with TypeScript 2.1, literal types are always
inferred for const variables and readonly properties.
Example
const c1 = 1; // Type 1
const c2 = c1; // Type 1
const c3 = "abc"; // Type "abc"
const c4 = true; // Type true
const c5 = cond ? 1 : "abc"; // Type 1 | "abc"
location has an explicit literal type annotation, the const variable gets
a non-widening literal type.
Example
Example
class Base {
x: number;
constructor() {
// return a new object other than `this`
return {
x: 1,
};
}
}
Generates:
var Derived = (function (_super) {
__extends(Derived, _super);
function Derived() {
var _this = _super.call(this) || this;
_this.x = 2;
return _this;
}
return Derived;
})(Base);
Example
configs/base.json :
`tsconfig.json`:
```json tsconfig
{
"extends": "./configs/base",
"files": ["main.ts", "supplemental.ts"]
}
tsconfig.nostrictnull.json :
## TypeScript 2.0
TypeScript has two special types, Null and Undefined, that have the values `null`
and `undefined` respectively.
Previously it was not possible to explicitly name these types, but `null` and
`undefined` may now be used as type names regardless of type checking mode.
### `--strictNullChecks`
In strict null checking mode, the `null` and `undefined` values are _not_ in the
domain of every type and are only assignable to themselves and `any` (the one
exception being that `undefined` is also assignable to `void`).
So, whereas `T` and `T | undefined` are considered synonymous in regular type
checking mode (because `undefined` is considered a subtype of any `T`), they are
different types in strict type checking mode, and only `T | undefined` permits
`undefined` values. The same is true for the relationship of `T` to `T | null`.
#### Example
```ts
// Compiled with --strictNullChecks
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1; // Ok
y = 1; // Ok
z = 1; // Ok
x = undefined; // Error
y = undefined; // Ok
z = undefined; // Ok
x = null; // Error
y = null; // Error
z = null; // Ok
x = y; // Error
x = z; // Error
y = x; // Ok
y = z; // Error
z = x; // Ok
z = 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 include undefined to be preceded
by an assignment to that variable in every possible preceding code
path.
Example
// Compiled with --strictNullChecks
let x: number;
let y: number | null;
let z: number | undefined;
x; // Error, reference not preceded by assignment
y; // Error, reference not preceded by assignment
z; // Ok
x = 1;
y = null;
x; // Ok
y; // Ok
t l t d t
Optional parameters and properties
Optional parameters and properties automatically have undefined added
to their types, even when their type annotations don't specifically
include undefined . For example, the following two types are identical:
Example
// Compiled with --strictNullChecks
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
f(x); // Ok, type of x is number here
} else {
f(x); // Error, type of x is number? here
}
let a = x != null ? f(x) : ""; // Type of a is string
let b = x && f(x); // Type of b is string | 0 | null | undefined
Example
interface Options {
location?: {
x?: number;
y?: number;
};
}
Type guards for dotted names also work with user defined type guard
functions and the typeof and instanceof operators and do not depend
on the --strictNullChecks compiler option.
Expression operators
Expression operators permit operand types to include null and/or
undefined but always produce values of non-null and non-undefined
types.
The && operator adds null and/or undefined to the type of the
right operand depending on which are present in the type of the left
operand, and the || operator removes both null and undefined from
the type of the left operand in the resulting union type.
Type widening
The null and undefined types are not widened to any in strict null
checking mode.
because of widening, but in strict null checking mode the inferred type
of z is null (and therefore, absent a type annotation, null is the
only possible value for z ).
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, the null and undefined 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 to if statements and ?: conditional
expressions and didn't include effects of assignments and control flow
constructs such as return and break 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 (the
narrowed type) at any given location for a local variable or parameter
that is declared to have a union type.
Example
function foo(x: string | number | boolean) {
if (typeof x === "string") {
x; // type of x is string here
x = 1;
x; // type of x is number here
}
x; // type of x is number | boolean here
}
Example
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
itself).
In a function expression or arrow function with no return type
annotation, if the function has no return statements, or only
return statements with expressions of type never , and if the end
point of the function is not reachable (as determined by control
flow analysis), the inferred return type for the function is
never .
In a function with an explicit never return type annotation, all
return statements (if any) must have expressions of type never
Example
interface Point {
readonly x: number;
readonly y: number;
}
var p2 = { x: 1, y: 1 };
var p3: Point = p2; // Ok, read-only alias for p2
p3.x = 5; // Error, p3.x is read-only
p2.x = 5; // Ok, but also changes p3.x because of aliasing
class Foo {
readonly a = 1;
readonly b: string;
constructor() {
this.b = "hello"; // Assignment permitted in constructor
}
}
they expect.
parameters are fake parameters that come first in the parameter list
of a function:
Example
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
--noImplicitThis
A new flag is also added in TypeScript 2.0 to flag all uses of this
Example
```json tsconfig { "compilerOptions": { "module": "commonjs",
"noImplicitAny": true, "removeComments": true, "preserveConstEnums":
true, "outFile": "../../built/local/tsc.js", "sourceMap": true },
"include": ["src//*"], "exclude": ["node_modules", "/*.spec.ts"] }
The supported glob wildcards are:
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` if `allowJs` is set to true).
If the `"files"` and `"include"` 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 the `"exclude"` property.
JS files (`.js` and `.jsx`) are also included if `allowJs` is set to true.
If the `"files"` or `"include"` properties are specified, the compiler will instead
include the union of the files included by those two properties.
Files in the directory specified using the `"outDir"` compiler option are always
excluded unless explicitly included via the `"files"` property (even when the
"`exclude`" property is specified).
Files included using `"include"` can be filtered using the `"exclude"` property.
However, files included explicitly using the `"files"` property are always included
regardless of `"exclude"`.
The `"exclude"` property defaults to excluding the `node_modules`,
`bower_components`, and `jspm_packages` directories when not specified.
TypeScript 2.0 provides a set of additional module resolution knops to _inform_ the
compiler where to find declarations for a given module.
#### Example
```json tsconfig
{
"compilerOptions": {
"baseUrl": "./modules"
}
}
Path mapping
Sometimes modules are not directly located under baseUrl. Loaders use
a mapping configuration to map module names to files at run-time, see
RequireJs documentation and SystemJS documentation.
Example
For instance, an import to a module "jquery" would be translated at
runtime to "node_modules/jquery/dist/jquery.slim.min.js" .
Using 'rootDirs', you can inform the compiler of the _roots_ making up this
"virtual" directory;
and thus the compiler can resolve relative modules imports within these "virtual"
directories _as if_ were merged together in one directory.
#### Example
```tree
src
└── views
└── view1.ts (imports './template1')
└── view2.ts
generated
└── templates
└── views
└── template1.ts (imports './view2')
`--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
declare module "hot-new-module";
All imports from a shorthand module will have the any type.
Example
declare module "*!text" {
const content: string;
export default content;
}
// Some do it the other way around.
declare module "json!*" {
const value: any;
export default value;
}
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 as any .
l
Example
declare module "myLibrary/*";
For example:
math-lib.d.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
class Bar {
a: number;
b?: number;
f() {
return 1;
}
g?(): number; // Body of optional method can be omitted
h?() {
return 2;
}
}
Example
class Singleton {
private static instance: Singleton;
private constructor() {}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
Example
abstract class Base {
abstract name: string;
abstract get value();
abstract set value(v: number);
}
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:
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
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
l
Example
tsc --target es5 --lib es5,es2015.promise
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 under `--
noUnusedLocals`.
#### Example
```ts
import B, { readFile } from "./b";
// ^ Error: `B` declared but never used
readFile();
function returnNull(_a) {
// OK
return null;
}
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 as import d from
in ./moduleA.ts or ./moduleA.d.ts .
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 like rollup.
Trailing commas in function
parameter and argument lists
Trailing comma in function parameter and argument lists are now
allowed. This is an implementation for a Stage-3 ECMAScript proposal
that emits down to valid ES3/ES5/ES6.
Example
function foo(
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 new --skipLibCheck 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 when --skipLibCheck 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.
Example
interface Error {
stack?: string;
}
interface Error {
code?: string;
path?: string;
stack?: string; // OK
}
New --declarationDir
--declarationDir allows for generating declaration files in a different
location than JavaScript files.
TypeScript 1.8
Type parameters as
constraints
With TypeScript 1.8 it becomes possible for a type parameter constraint
to reference type parameters from the same type parameter list.
Previously this was an error. This capability is usually referred to as
F-Bounded Polymorphism.
Example
let x = { a: 1, b: 2, c: 3, d: 4 };
assign(x, { b: 10, d: 20 });
assign(x, { e: 0 }); // Error
Control flow analysis errors
TypeScript 1.8 introduces control flow analysis to help catch common
errors that users tend to run into. Read on to get more details, and
check out these errors in action:
Unreachable code
Statements guaranteed to not be executed at run time are now
correctly flagged as unreachable code errors. For instance, statements
following unconditional return , throw , break or continue statements
are considered unreachable. Use --allowUnreachableCode to disable
unreachable code detection and reporting.
Example
function f() {
return; // Automatic Semicolon Insertion triggered at newline
{
x: "string"; // Error: Unreachable code detected.
}
}
Unused labels
Unused labels are also flagged. Just like unreachable code checks,
these are turned on by default; use --allowUnusedLabels to stop
reporting these errors.
Example
Implicit returns
Functions with code paths that do not return a value in JS implicitly
return undefined . These can now be flagged by the compiler as implicit
returns. The check is turned off by default; use --noImplicitReturns to
turn it on.
Example
function f(x) {
// Error: Not all code paths return a value.
if (x) {
return false;
}
Example
switch (x % 2) {
case 0: // Error: Fallthrough case in switch.
console.log("even");
case 1:
console.log("odd");
break;
}
case 2:
console.log("This is *two much*!");
break;
}
Stateless Function Components
in React
TypeScript now supports Stateless Function components. These are
lightweight components that easily compose other components:
// Use parameter destructuring and defaults for easy definition of 'props' type
const Greeter = ({ name = "world" }) => <div>Hello, {name}!</div>;
For this feature and simplified props, be sure to be use the latest
version of react.d.ts.
Simplified props type
management in React
In TypeScript 1.8 with the latest version of react.d.ts (see above),
we've also greatly simplified the declaration of props types.
Specifically:
The ref and key properties will appear with correct types on all
components
The ref property is correctly disallowed on instances of Stateless
Function components
Augmenting global/module
scope from modules
Users can now declare any augmentations that they want to make, or
that any other consumers already have made, to an existing module.
Module augmentations look like plain old ambient module declarations
(i.e. the declare module "foo" { } syntax), and are directly nested either
your own modules, or in another top level ambient external module.
Example
Here map.ts can declare that it will internally patch the Observable type
from observable.ts and add the map method to it.
// observable.ts
export class Observable<T> {
// ...
}
// map.ts
import { Observable } from "./observable";
Observable.prototype.map = /*...*/;
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());
Example
declare global {
interface Array<T> {
mapToNumbers(): number[];
}
}
Array.prototype.mapToNumbers = function () {
/* ... */
};
String literal types
It's not uncommon for an API to expect a specific set of strings for
certain values. For instance, consider a UI library that can move
elements across the screen while controlling the "easing" of the
animation.
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: string; // Can be "ease-in", "ease-out", "ease-in-out"
}
// No errors
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });
With TypeScript 1.8, we've introduced string literal types. These types
are written the same way string literals are, but in type positions.
Users can now ensure that the type system will catch such errors.
Here's our new AnimationOptions using string literal types:
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: "ease-in" | "ease-out" | "ease-in-out";
}
Example
will concatenate all modules in the compilation into a single output file
containing multiple module closures.
A module name will be computed for each module based on its relative
location to rootDir .
Example
// file src/a.ts
import * as B from "./lib/b";
export function createA() {
return B.createB();
}
// file src/lib/b.ts
export function createB() {
return {};
}
Results in:
define("lib/b", ["require", "exports"], function (require, exports) {
"use strict";
function createB() {
return {};
}
exports.createB = createB;
});
define("a", ["require", "exports", "lib/b"], function (require, exports, B) {
"use strict";
function createA() {
return B.createB();
}
exports.createA = createA;
});
Support for default import
interop with SystemJS
Module loaders like SystemJS wrap CommonJS modules and expose then
as a default ES6 import. This makes it impossible to share the
definition files between the SystemJS and CommonJS implementation of
the module as the module shape looks different based on the loader.
Example
is compiled to:
And results in
0
1
2
3
4
Improved checking for
for..in statements
Previously the type of a for..in variable is inferred to any ; that
allowed the compiler to ignore invalid uses within the for..in body.
Example
var a: MyObject[];
for (var x in a) {
// Type of x is implicitly string
var obj = a[x]; // Type of obj is MyObject
}
Modules are now emitted with
a "use strict"; prologue
Modules were always parsed in strict mode as per ES6, but for non-ES6
targets this was not respected in the generated code. Starting with
TypeScript 1.8, emitted modules are always in strict mode. This
shouldn't have any visible changes in most code as TS considers most
strict mode errors as errors at compile time, but it means that some
things which used to silently fail at runtime in your TS code, like
assigning to NaN , will now loudly fail. You can reference the MDN
Article on strict mode for a detailed list of the differences between
strict mode and non-strict mode.
Including .js files with --
allowJs
Often there are external source files in your project that may not be
authored in TypeScript. Alternatively, you might be in the middle of
converting a JS code base into TS, but still want to bundle all your
JS code into a single file with the output of your new TS code.
.js files are now allowed as input to tsc . The TypeScript compiler
checks the input .js files for syntax errors, and emits valid output
based on the --target and --module flags. The output can be
combined with other .ts files as well. Source maps are still generated
for .js files just like with .ts files.
Custom JSX factories using -
-reactNamespace
Passing --reactNamespace <JSX factory Name> along with --jsx react allows for
using a different JSX factory from the default React .
The new factory name will be used to call createElement and __spread
functions.
Example
Compiled with:
Results in:
"use strict";
var jsxFactory_1 = require("jsxFactory");
var div = jsxFactory_1.jsxFactory.createElement("div", null, "Hello JSX!");
this -based type guards
TypeScript 1.8 extends user-defined type guard functions to class and
interface methods.
Example
class FileSystemObject {
isFile(): this is File {
return this instanceof File;
}
isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked(): this is Networked & this {
return this.networked;
}
constructor(public path: string, private networked: boolean) {}
}
Microsoft.TypeScript.Compiler
Microsoft.TypeScript.MSBuild
TypeScript-Preview
Prettier error messages from
tsc
We understand that a ton of monochrome output can be a little
difficult on the eyes. Colors can help discern where a message starts
and ends, and these visual clues are important when error output gets
overwhelming.
The old behavior still remains the same if given a directory - the
compiler will try to find a file in the directory named tsconfig.json .
Allow comments in
tsconfig.json
It's always nice to be able to document your configuration!
tsconfig.json now accepts single and multi-line comments.
{
"compilerOptions": {
"target": "ES2015", // running on node v5, yaay!
"sourceMap": true // makes debugging easier
},
/*
* Excluded files
*/
"exclude": [
"file.d.ts"
]
}
Support output to IPC-
driven files
TypeScript 1.8 allows users to use the --outFile argument with special
file system entities like named pipes, devices, etc.
A couple of limitations:
If you add a tsconfig.json file, TypeScript files that are not
considered part of that context are not compiled.
Apache Cordova Apps still have the existing limitation of a single
tsconfig.json file, which must be in either the root or the scripts
folder.
There is no template for tsconfig.json in most project types.
TypeScript 1.7
async / await
support in ES6
targets (Node v4+)
TypeScript now supports asynchronous functions for engines that have
native support for ES6 generators, e.g. Node v4 and above.
Asynchronous functions are prefixed with the async keyword; await
Example
In the following example, each input element will be printed out one
at a time with a 200ms delay:
"use strict";
// printDelayed is a 'Promise<void>'
async function printDelayed(elements: string[]) {
for (const element of elements) {
await delay(200);
console.log(element);
}
}
module flag and allows you to specify the module output when targeting
ES6 . This provides more flexibility to target exactly the features you
want in specific runtimes.
Example
It is a common pattern to return the current object (i.e. `this`) from a method to
create [fluent-style APIs](https://wikipedia.org/wiki/Fluent_interface).
For instance, consider the following `BasicCalculator` module:
```ts
export default class BasicCalculator {
public constructor(protected value: number = 0) {}
public square() {
this.value = this.value ** 2;
return this;
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
}
method.
For instance:
interface MyType {
extend<T>(other: T): this & T;
}
ES7 exponentiation operator
TypeScript 1.7 supports upcoming ES7/ES2016 exponentiation operators:
** and **= . The operators will be transformed in the output to
ES3/ES5 using Math.pow .
Example
var x = 2 ** 3;
var y = 10;
y **= 2;
var z = -(4 ** 3);
Example
// Type of f1 is (arg?: { x?: number, y?: number }) => void
function f1({ x = 0, y = 0 } = {}) {}
f2();
f2({}); // Error, x not optional
f2({ x: 1 });
f2({ y: 1 }); // Error, x not optional
f2({ x: 1, y: 1 });
Support for decorators when
targeting ES3
Decorators are now allowed when targeting ES3. TypeScript 1.7 removes
the ES5-specific use of reduceRight from the __decorate helper. The
changes also inline calls to Object.getOwnPropertyDescriptor and
Object.defineProperty in a backwards-compatible fashion that allows for
clean up of the emit for ES5 and later by removing various repetitive
calls to the aforementioned Object methods.
TypeScript 1.6
JSX support
JSX is an embeddable XML-like syntax. It is meant to be transformed
into valid JavaScript, but the semantics of that transformation are
implementation-specific. JSX came to popularity with the React library
but has since seen other applications. TypeScript 1.6 supports
embedding, type checking, and optionally compiling JSX directly into
JavaScript.
var x = <any>foo;
// is equivalent to:
var x = foo as any;
Using React
To use JSX-support with React you should use the React typings.
These typings define the JSX namespace so that TypeScript can
correctly check JSX expressions for React. For example:
/// <reference path="react.d.ts" />
interface Props {
name: string;
}
namespace. Please see the [[JSX]] wiki page for defining the JSX
Output generation
TypeScript ships with two JSX modes: preserve and react .
See the [[JSX]] wiki page for more information on using JSX in
TypeScript.
Intersection types
TypeScript 1.6 introduces intersection types, the logical complement of
union types. A union type A | B represents an entity that is either of
type A or type B , whereas an intersection type A & B represents an
entity that is both of type A and type B .
Example
interface Person {
name: string;
}
function f() {
if (true) {
interface T {
x: number;
}
let v: T;
v.x = 5;
} else {
interface T {
x: string;
}
let v: T;
v.x = "hello";
}
}
Local types may reference enclosing type parameters and local class
and interfaces may themselves be generic. For example:
function f3() {
function f<X, Y>(x: X, y: Y) {
class C {
public x = x;
public y = y;
}
return C;
}
let C = f(10, "hello");
let v = new C();
let x = v.x; // number
let y = v.y; // string
}
Class expressions
TypeScript 1.6 adds support for ES6 class expressions. In a class
expression, the class name is optional and, if specified, is only in scope
in the class expression itself. This is similar to the optional name of a
function expression. It is not possible to refer to the class instance
type of a class expression outside the class expression, but the type
can of course be matched structurally. For example:
Some examples:
// Extend built-in types
class ThingA {
getGreeting() {
return "Hello from A";
}
}
class ThingB {
getGreeting() {
return "Hello from B";
}
}
interface Greeter {
getGreeting(): string;
}
interface GreeterConstructor {
new (): Greeter;
}
Examples
var s: Lazy<string>;
s = "eager";
s = () => "lazy";
Examples
function* g() {
for (var i = 0; i < 100; i++) {
yield ""; // infer string
}
yield* otherStringGenerator(); // infer element type of otherStringGenerator
}
Experimental support for
async functions
TypeScript 1.6 introduces experimental support of async functions when
targeting ES6. Async functions are expected to invoke an asynchronous
operation and await its result without blocking normal execution of the
program. This accomplished through the use of an ES6-compatible
Promise implementation, and transposition of the function body into a
compatible form to resume execution when the awaited asynchronous
operation completes.
Example
var p: Promise<number> = /* ... */;
async function fn(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"
return 1 + i;
}
class C {
async m(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type
"number"
return 1 + i;
}
interface Foo {
y: string;
}
Examples
## TypeScript 1.5
## ES6 Modules
TypeScript 1.5 supports ECMAScript 6 (ES6) modules. ES6 modules are effectively
TypeScript external modules with a new syntax: ES6 modules are separately loaded
source files that possibly import other modules and provide a number of externally
accessible exports. ES6 modules feature several new export and import declarations.
It is recommended that TypeScript libraries and applications be updated to use the
new syntax, but this is not a requirement. The new ES6 module syntax coexists with
TypeScript's original internal and external module constructs and the constructs
can be mixed and matched at will.
```ts
interface Stream { ... }
function writeToStream(stream: Stream, data: string) { ... }
export { Stream, writeToStream as write }; // writeToStream exported as write
Re-exporting
Using from clause a module can copy the exports of a given module
to the current module without introducing local names.
Default Export
An export default declaration specifies an expression that becomes the
default export of a module:
t
Bare Import
A "bare import" can be used to import a module only for its side-
effects.
import "./polyfills";
For more information about module, please see the ES6 module support
spec.
Destructuring in declarations
and assignments
TypeScript 1.5 adds support to ES6 destructuring declarations and
assignments.
Declarations
A destructuring declaration introduces one or more named variables
and initializes them with values extracted from properties of an object
or elements of an array.
var { x, y, z } = getSomeObject();
function drawText({ text = "", location: [x, y] = [0, 0], bold = false }) {
// Draw text
}
Assignments
Destructuring patterns can also be used in regular assignment
expressions. For instance, swapping two variables can be written as a
single destructuring assignment:
var x = 1;
var y = 2;
[x, y] = [y, x];
namespace keyword
TypeScript used the module keyword to define both "internal modules"
and "external modules"; this has been a bit of confusion for
developers new to TypeScript. "Internal modules" are closer to what
most people would call a namespace; likewise, "external modules" in JS
speak really just are modules now.
Before:
module Math {
export function add(x, y) { ... }
}
After:
namespace Math {
export function add(x, y) { ... }
}
let and const support
ES6 let and const declarations are now supported when targeting ES3
and ES5.
Const
const MAX = 100;
Block scoped
if (true) {
let a = 4;
// use a
} else {
let a = "string";
// use a
}
Example
A decorator is:
an expression
that evaluates to a function
that takes the target, name, and property descriptor as arguments
and optionally returns a property descriptor to install on the
target object
Example
class C {
@readonly
@enumerable(false)
method() {}
}
function enumerable(value) {
return function (target, key, descriptor) {
descriptor.enumerable = value;
};
}
Computed properties
Initializing an object with dynamic properties can be a bit of a
burden. Take the following example:
Usage:
and
With ES6’s codepoint escapes, you can cleanly represent that exact
character in strings and template strings with a single escape:
"\u{20bb7}" . TypeScript will emit the string in ES3/ES5 as
"\uD842\uDFB7" .
Tagged template strings in
ES3/ES5
In TypeScript 1.4, we added support for template strings for all
targets, and tagged templates for just ES6. Thanks to some
considerable work done by @ivogabe, we bridged the gap for for
tagged templates in ES3 and ES5.
will be emitted as
Generated JS code:
Example
Option `--outDir` duplicates the input hierarchy in the output. The compiler
computes the root of the input files as the longest common path of all input files;
and then uses that to replicate all its substructure in the output.
The TypeScript compiler emits a few helpers like `__extends` when needed. The
helpers are emitted in every file they are referenced in. If you want to
consolidate all helpers in one place, or override the default behavior, use `--
noEmitHelpers` to instructs the compiler not to emit them.
By default the output new line character is `\r\n` on Windows based systems and
`\n` on \*nix based systems. `--newLine` command line flag allows overriding this
behavior and specifying the new line character to be used in generated output
files.
## TypeScript 1.4
## Union types
### Overview
Union types are a powerful way to express a value that can be one of several types.
For example, you might have an API for running a program that takes a commandline
as either a `string`, a `string[]` or a function that returns a `string`. You can
now write:
```ts
interface RunOptions {
program: string;
commandline: string[] | string | (() => string);
}
When reading from a union type, you can see any properties that are
shared by them:
if (opts.length === 0) {
// OK, string and string[] both have 'length' property
console.log("it's empty");
}
Using Type Guards, you can easily work with a variable of a union type:
Stricter Generics
With union types able to represent a wide range of type scenarios,
we've decided to improve the strictness of certain generic calls.
Previously, code like this would (surprisingly) compile without error:
function equal<T>(lhs: T, rhs: T): boolean {
return lhs === rhs;
}
// Previously: No error
// New behavior: Error, no best common type between 'string' and 'number'
var e = equal(42, "hello");
With union types, you can now specify the desired behavior at both the
function declaration site and the call site:
if (foo) {
console.log(x); // Error, cannot refer to x before its declaration
let x = "hello";
} else {
console.log(x); // Error, x is not declared in this block
}
Type aliases are exactly the same as their original types; they are
simply alternative names.
const enum (completely inlined
enums)
Enums are very useful, but some programs don't actually need the
generated code and would benefit from simply inlining all instances of
enum members with their numeric equivalents. The new const enum
declaration works just like a regular enum for type safety, but erases
completely at compile time.
Compiles to exactly:
var d = 1;
enum MyFlags {
None = 0,
Neat = 1,
Cool = 2,
Awesome = 4,
Best = Neat | Cool | Awesome,
}
var b = MyFlags.Best; // emits var b = 7;
-noEmitOnError commandline
option
The default behavior for the TypeScript compiler is to still emit .js
files if there were type errors (for example, an attempt to assign a
string to a number ). This can be undesirable on build servers or
other scenarios where only output from a "clean" build is desired. The
new flag noEmitOnError prevents the compiler from emitting .js code if
there were any errors.
This is now the default for MSBuild projects; this allows MSBuild
incremental build to work as expected, as outputs are only generated
on clean builds.
AMD Module names
By default AMD modules are generated anonymous. This can lead to
problems when other tools are used to process the resulting modules
like a bundler (e.g. r.js ).
The new amd-module name tag allows passing an optional module name to
the compiler:
//// [amdModule.ts]
///<amd-module name='NamedModule'/>
export class C {}
//// [amdModule.js]
define("NamedModule", ["require", "exports"], function (require, exports) {
var C = (function () {
function C() {}
return C;
})();
exports.C = C;
});
TypeScript 1.3
Protected
The new protected modifier in classes works like it does in familiar
languages like C++, C#, and Java. A protected member of a class is
visible only inside subclasses of the class in which it is declared:
class Thing {
protected doSomething() {
/* ... */
}
}
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
Note that in TypeScript 1.4, when accessing an element outside the set
of known indices, a union type is used instead:
x[3] = "world"; // OK
console.log(x[5].toString()); // OK, 'string' and 'number' both have toString
x[6] = true; // Error, boolean isn't number or string
TypeScript 1.1
Performance Improvements
The 1.1 compiler is typically around 4x faster than any previous release.
See this blog post for some impressive charts.
Better Module Visibility Rules
TypeScript now only strictly enforces the visibility of types in modules
if the --declaration flag is provided. This is very useful for Angular
scenarios, for example:
module MyControllers {
interface ZooScope extends ng.IScope {
animals: Animal[];
}
export class ZooController {
// Used to be an error (cannot expose ZooScope), but now is only
// an error when trying to generate .d.ts files
constructor(public $scope: ZooScope) {}
/* more code */
}
}
Template Literal Types
String literal types in TypeScript allow us to model functions and APIs
that expect a set of specific strings.
setVerticalAlignment("middel");
This is pretty nice because string literal types can basically spell-check our
string values.
We also like that string literals can be used as property names in mapped types.
In this sense, they're also usable as building blocks:
```ts
type Options = {
[K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean;
};
// same as
// type Options = {
// noImplicitAny?: boolean,
// strictNullChecks?: boolean,
// strictFunctionTypes?: boolean
// };
But there's another place that that string literal types could be used
as building blocks: building other string literal types.
That's why TypeScript 4.1 brings the template literal string type. It
has the same syntax as template literal strings in JavaScript, but is
used in type positions. When you use it with concrete literal types, it
produces a new string literal type by concatenating the contents.
```ts twoslash
type Color = "red" | "blue";
type Quantity = "one" | "two";
This can be used beyond cute examples in release notes. For example,
several libraries for UI components have a way to specify both vertical
and horizontal alignment in their APIs, often with both at once using a
single string like "bottom-right" . Between vertically aligning with "top" ,
"middle" , and "bottom" , and horizontally aligning with "left" ,
"center" , and "right" , there are 9 possible strings where each of the
former strings is connected with each of the latter strings using a
dash.
Some of the real value comes from dynamically creating new string literals.
For example, imagine a `makeWatchedObject` API that takes an object and produces a
mostly identical object, but with a new `on` method to detect for changes to the
properties.
```ts
let person = makeWatchedObject({
firstName: "Homer",
age: 42, // give-or-take
location: "Springfield",
});
person.on("firstNameChanged", () => {
console.log(`firstName was changed!`);
});
/// Create a "watched object" with an 'on' method /// so that you
can watch for changes to properties. declare function
makeWatchedObject(obj: T): T & PropEventSource;
With this, we can build something that errors when we give the wrong property!
```ts twoslash
// @errors: 2345
type PropEventSource<T> = {
on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
};
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
let person = makeWatchedObject({
firstName: "Homer",
age: 42, // give-or-take
location: "Springfield",
});
// ---cut---
// error!
person.on("firstName", () => {});
// error!
person.on("frstNameChanged", () => {});
``ts twoslash type PropEventSource<T> = { on<K extends string & keyof T>
(eventName: ${K}Changed`, callback: (newValue: T[K]) => void ): void; };
```ts twoslash
type EnthusiasticGreeting<T extends string> = `${Uppercase<T>}`
For more details, see the original pull request and the in-progress pull
request to switch to type alias helpers.
Key Remapping in Mapped
Types
Just as a refresher, a mapped type can create new object types based
on arbitrary keys
type Options = {
[K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean;
};
// same as
// type Options = {
// noImplicitAny?: boolean,
// strictNullChecks?: boolean,
// strictFunctionTypes?: boolean
// };
/// 'Partial<T>' is the same as 'T', but with each property marked optional.
type Partial<T> = {
[K in keyof T]?: T[K];
};
Until now, mapped types could only produce new object types with keys
that you provided them; however, lots of the time you want to be able
to create new keys, or filter out keys, based on the inputs.
That's why TypeScript 4.1 allows you to re-map keys in mapped types
with a new as clause.
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
// ^^^^^^^^^^^^^
// This is the new syntax!
}
With this new as clause, you can leverage features like template literal
types to easily create property names based off of old ones.
```ts twoslash
// Remove the 'kind' property
type RemoveKindField<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K]
};
interface Circle {
kind: "circle";
radius: number;
}
For more information, take a look at the original pull request over on
GitHub.
Recursive Conditional Types
In JavaScript it's fairly common to see functions that can flatten and
build up container types at arbitrary levels. For example, consider the
.then() method on instances of Promise . .then(...) unwraps each
promise until it finds a value that's not "promise-like", and passes
that value to a callback. There's also a relatively new flat method on
Array s that can take a depth of how deep to flatten.
Keep in mind that while these recursive types are powerful, but they
should be used responsibly and sparingly.
First off, these types can do a lot of work which means that they can
increase type-checking time. Trying to model numbers in the Collatz
conjecture or Fibonacci sequence might be fun, but don't ship that in
.d.ts files on npm.
But apart from being computationally intensive, these types can hit an
internal recursion depth limit on sufficiently-complex inputs. When that
recursion limit is hit, that results in a compile-time error. In general,
it's better not to use these types at all than to write something that
fails on more realistic examples.
// These are all allowed too! // They have the type 'string |
number'. opts.yadda.toString(); opts["foo bar baz"].toString();
opts[Math.random()].toString(); }
In the above example, `Options` has an index signature that says any accessed
property that's not already listed should have the type `string | number`.
This is often convenient for optimistic code that assumes you know what you're
doing, but the truth is that most values in JavaScript do not support every
potential property name.
Most types will not, for example, have a value for a property key created by
`Math.random()` like in the previous example.
For many users, this behavior was undesirable, and felt like it wasn't leveraging
the full strict-checking of `--strictNullChecks`.
That's why TypeScript 4.1 ships with a new flag called `--
noUncheckedIndexedAccess`.
Under this new mode, every property access (like `foo.bar`) or indexed access (like
`foo["bar"]`) is considered potentially undefined.
That means that in our last example, `opts.yadda` will have the type `string |
number | undefined` as opposed to just `string | number`.
If you need to access that property, you'll either have to check for its existence
first or use a non-null assertion operator (the postfix `!` character).
```ts twoslash
// @errors: 2532
// @noUncheckedIndexedAccess
interface Options {
path: string;
permissions: number;
If you don't need the indexes, you can iterate over individual elements by using a
`for`-`of` loop or a `forEach` call.
```ts twoslash
// @noUncheckedIndexedAccess
function screamLines(strs: string[]) {
// This works fine
for (const str of strs) {
console.log(str.toUpperCase());
}
In TypeScript 4.1, the paths option can be used without baseUrl . This
helps avoid some of these issues.
checkJs Implies allowJs
Previously if you were starting a checked JavaScript project, you had
to set both allowJs and checkJs . This was a slightly annoying bit of
friction in the experience, so checkJs now implies allowJs by default.
react-jsx
react-jsxdev
and one for development builds might look like the following:
```json tsconfig
// ./src/tsconfig.dev.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react-jsxdev"
}
}
// @filename: first.ts
export class C {}
// @filename: main.ts
import * as first from "./first";
/**
* @see first.C
*/
function related() {}
lib.d.ts Changes
lib.d.ts may have a set of changed APIs, potentially in part due to
how the DOM types are automatically generated. One specific change is
that Reflect.enumerate has been removed, as it was removed from
ES2016.
was any or unknown , the type of the whole that expression would be
the type of somethingElse .
For example, previously the type for x here was { someProp: string } .
resolve()
~~~~~~~~~
error TS2554: Expected 1 arguments, but got 0.
An argument for 'value' was not provided.
interface Animal {
name: string;
owner: Person;
}
This modeled exactly how the operation would occur: if pet was
defined, all the properties from Person would be present; otherwise,
none of them would be defined on the result. It was an all-or-
nothing operation.
However, we've seen this pattern taken to the extreme, with hundreds
of spreads in a single object, each spread potentially adding in
hundreds or thousands of properties. It turns out that for various
reasons, this ends up being extremely expensive, and usually for not
much benefit.
{
x: number;
name?: string;
age?: number;
location?: string;
}
For more details, see the original change. While this behavior is not
entirely consistent right now, we expect a future release will produce
cleaner and more predictable results.
Also consider tail , that takes an array or tuple, and returns all
elements but the first.
function tail(arg) {
const [_, ...result] = arg;
return result;
}
For concat , the only valid thing we could do in older versions of the
language was to try and write some overloads.
But that signature doesn't encode anything about the lengths of the
input, or the order of the elements, when using tuples.
The first change is that spreads in tuple type syntax can now be
generic. This means that we can represent higher-order operations on
tuples and arrays even when we don't know the actual types we're
operating over. When generic spreads are instantiated (or, replaced
with a real type) in these tuple types, they can produce other sets of
array and tuple types.
For example, that means we can type function like tail , without our
"death by a thousand overloads" issue.
```ts twoslash function tail(arr: readonly [any, …T]) { const [_ignored,
…rest] = arr; return rest; }
const r1 = tail(myTuple); // ^?
The second change is that rest elements can occur anywhere in a tuple - not just at
the end!
```ts
type Strings = [string, string];
type Numbers = [number, number];
```js
function partialCall(f, ...headArgs) {
return (...tailArgs) => f(...headArgs, ...tailArgs);
}
TypeScript 4.0 improves the inference process for rest parameters and
rest tuple elements so that we can type this and have it "just work".
```ts twoslash
// @errors: 2345 2554 2554 2345
type Arr = readonly unknown[];
// This works!
const f3 = partialCall(foo, "hello");
// ^?
// Works!
f3(123, true);
f3();
f3(123, "hello");
For example, the following function that uses a tuple type as a rest
parameter…
That's why in TypeScript 4.0, tuples types can now provide labels.
```ts
type Range = [start: number, end: number];
To deepen the connection between parameter lists and tuple types, the
syntax for rest elements and optional elements mirrors the syntax for
parameter lists.
There are a few rules when using labeled tuples. For one, when
labeling a tuple element, all other elements in the tuple must also be
labeled.
It's worth noting - labels don't require us to name our variables differently when
destructuring.
They're purely there for documentation and tooling.
```ts twoslash
function foo(x: [first: string, second: number]) {
// ...
To learn more, check out the pull request for labeled tuple elements.
Class Property Inference
from Constructors
TypeScript 4.0 can now use control flow analysis to determine the
types of properties in classes when noImplicitAny is enabled.
```ts twoslash class Square { // Previously both of these were any area;
// ^? sideLength; // ^? constructor(sideLength: number) {
this.sideLength = sideLength; this.area = sideLength ** 2; } }
In cases where not all paths of a constructor assign to an instance member, the
property is considered to potentially be `undefined`.
<!--prettier-ignore -->
```ts twoslash
// @errors: 2532
class Square {
sideLength;
// ^?
constructor(sideLength: number) {
if (Math.random()) {
this.sideLength = sideLength;
}
}
get area() {
return this.sideLength ** 2;
}
}
In cases where you know better (e.g. you have an initialize method
of some sort), you'll still need an explicit type annotation along with a
definite assignment assertion ( ! ) if you're in
strictPropertyInitialization .
```ts twoslash class Square { // definite assignment assertion // v
sideLength!: number; // ^^^^^^^^ // type annotation
```ts
// Addition
// a = a + b
a += b;
// Subtraction
// a = a - b
a -= b;
// Multiplication
// a = a * b
a *= b;
// Division
// a = a / b
a /= b;
// Exponentiation
// a = a ** b
a **= b;
These operators are great for substituting any example where a user
might write code like the following:
a = a && b;
a = a || b;
a = a ?? b;
There are even some patterns we've seen (or, uh, written ourselves)
to lazily initialize values, only if they'll be needed.
// After
(values ??= []).push("hello");
On the rare case that you use getters or setters with side-effects,
it's worth noting that these operators only perform assignments if
necessary. In that sense, not only is the right side of the operator
"short-circuited" - the assignment itself is too.
obj.prop ||= foo();
if (!obj.prop) {
obj.prop = foo();
}
Try running the following example to see how that differs from always
performing the assignment.
// Replace me!
return Math.random() < 0.5;
},
set prop(_val: boolean) {
console.log("setter has run");
}
};
For more details, you can [take a look at the pull request here]
(https://github.com/microsoft/TypeScript/pull/37727).
You can also [check out TC39's proposal repository for this feature]
(https://github.com/tc39/proposal-logical-assignment/).
Since the beginning days of TypeScript, `catch` clause variables have always been
typed as `any`.
This meant that TypeScript allowed you to do anything you wanted with them.
```ts twoslash
try {
// Do some work
} catch (x) {
// x has type 'any' - have fun!
console.log(x.message);
console.log(x.toUpperCase());
x++;
x.yadda.yadda.yadda();
}
That's why TypeScript 4.0 now lets you specify the type of catch
For more details you can [peek at the changes for this feature]
(https://github.com/microsoft/TypeScript/pull/39015).
In TypeScript 4.0, users can customize the fragment factory through the new
`jsxFragmentFactory` option.
```json5
{
compilerOptions: {
target: "esnext",
module: "commonjs",
jsx: "react",
jsxFactory: "h",
jsxFragmentFactory: "Fragment",
},
}
```tsx twoslash
// @noErrors
// @showEmit
// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */
We'd like to extend a big thanks to community member Noj Vek for
sending this pull request and patiently working with our team on it.
You can see that the pull request for more details!
Speed Improvements in build
mode with --noEmitOnError
Previously, compiling a program after a previous compile with errors
under --incremental would be extremely slow when using the --
noEmitOnError flag.
TypeScript 4.0 changes this which gives a great speed boost in these
scenarios, and in turn improves --build mode scenarios (which imply
both --incremental and --noEmitOnError ).
For more details, you can see the implementing pull request.
Editor Improvements
The TypeScript compiler doesn't only power the editing experience for
TypeScript itself in most major editors - it also powers the JavaScript
experience in the Visual Studio family of editors and more. For that
reason, much of our work focuses on improving editor scenarios - the
place you spend most of your time as a developer.
You can check out a partial list of editors that have support for
TypeScript to learn more about whether your favorite editor has
support to use new versions.
For more details, check out the pull request for this feature.
That's why we've been working on a new mode for editors to provide
a partial experience until the full language service experience has
loaded up. The core idea is that editors can run a lightweight partial
server that only looks at the current files that the editor has open.
It's hard to say precisely what sorts of improvements you'll see, but
anecdotally, it used to take anywhere between 20 seconds to a minute
before TypeScript would become fully responsive on the Visual Studio
Code codebase. In contrast, our new partial semantic mode seems
to bring that delay down to just a few seconds. As an example, in
the following video, you can see two side-by-side editors with
TypeScript 3.9 running on the left and TypeScript 4.0 running on the
right.
For more information, you can see the original proposal, the
implementing pull request, along with the follow-up meta issue.
Smarter Auto-Imports
Auto-import is a fantastic feature that makes coding a lot easier;
however, every time auto-import doesn't seem to work, it can throw
users off a lot. One specific issue that we heard from users was that
auto-imports didn't work on dependencies that were written in
TypeScript - that is, until they wrote at least one explicit import
somewhere else in their project.
Why would auto-imports work for @types packages, but not for
packages that ship their own types? It turns out that auto-imports
only work on packages your project already includes. Because
TypeScript has some quirky defaults that automatically add packages in
node_modules/@types to your project, those packages would be auto-
imported. On the other hand, other packages were excluded because
crawling through all your node_modules packages can be really expensive.
All of this leads to a pretty lousy getting started experience for when
you're trying to auto-import something that you've just installed but
haven't used yet.
In the rare cases when your package.json lists more than ten typed
dependencies that haven't been imported yet, this feature automatically
disables itself to prevent slow project loading. To force the feature
to work, or to disable it entirely, you should be able to configure your
editor. For Visual Studio Code, this is the "Include Package JSON Auto
Imports" (or typescript.preferences.includePackageJsonAutoImports ) setting.
For more details, you can see the proposal issue along with the
implementing pull request.
Our New Website!
The TypeScript website has recently been rewritten from the ground
up and rolled out!
We already wrote a bit about our new site, so you can read up more
there; but it's worth mentioning that we're still looking to hear what
you think! If you have questions, comments, or suggestions, you can
file them over on the website's issue tracker.
Breaking Changes
lib.d.ts Changes
Our lib.d.ts declarations have changed - most specifically, types for
the DOM have changed. The most notable change may be the removal
of document.origin which only worked in old versions of IE and Safari
MDN recommends moving to self.origin .
```ts twoslash // @errors: 1049 2610 class Base { get foo() { return 100;
} set foo(value) { // ... } }
```ts twoslash
// @errors: 2611
class Base {
prop = 10;
}
For more details, read up on the relevant pull request for this
change.
Improvements in Inference
and Promise.all
Recent versions of TypeScript (around 3.7) have had updates to the
declarations of functions like Promise.all and Promise.race .
Unfortunately, that introduced a few regressions, especially when mixing
in values with null or undefined .
interface Lion {
roar(): void;
}
interface Seal {
singKissFromARose(): void;
}
Thanks to a pull request from Jack Bates, this has been fixed with
improvements in our inference process in TypeScript 3.9. The above no
longer errors. If you've been stuck on older versions of TypeScript
due to issues around Promise s, we encourage you to give 3.9 a shot!
https://github.com/microsoft/TypeScript/pull/36576
https://github.com/microsoft/TypeScript/pull/36590
https://github.com/microsoft/TypeScript/pull/36607
https://github.com/microsoft/TypeScript/pull/36622
https://github.com/microsoft/TypeScript/pull/36754
https://github.com/microsoft/TypeScript/pull/36696
While there's still room for improvement, we hope this work translates
to a snappier experience for everyone!
// @ts-expect-error Comments
Imagine that we're writing a library in TypeScript and we're exporting
some function called doStuff as part of our public API. The
function's types declare that it takes two string s so that other
TypeScript users can get type-checking errors, but it also does a
runtime check (maybe only in development builds) to give JavaScript
users a helpful error.
// do some stuff
}
expect(() => {
doStuff(123, 456);
}).toThrow();
doStuff(123, 456);
// ~~~
// error: Type 'number' is not assignable to type 'string'.
// @ts-expect-error
console.log(47 * "octopus");
// @ts-expect-error
console.log(1 + 1);
ts-ignore or ts-expect-error ?
In some ways // @ts-expect-error can act as a suppression comment,
similar to // @ts-ignore . The difference is that // @ts-ignore will do
nothing if the following line is error-free.
you're writing test code where you actually want the type system
to error on an operation
you expect a fix to be coming in fairly quickly and you just need
a quick workaround
you're in a reasonably-sized project with a proactive team that
wants to remove suppression comments as soon affected code is
valid again
you have a larger project and new errors have appeared in code
with no clear owner
you are in the middle of an upgrade between two different
versions of TypeScript, and a line of code errors in one version
but not another.
you honestly don't have the time to decide which of these options
is better.
Uncalled Function Checks in
Conditional Expressions
In TypeScript 3.7 we introduced uncalled function checks to report an
error when you've forgotten to call a function.
// Oops!
if (hasImportantPermissions) {
// ~~~~~~~~~~~~~~~~~~~~~~~
// This condition will always return true since the function is always defined.
// Did you mean to call it instead?
deleteAllTheImportantFiles();
}
falseExpr syntax).
declare function listFilesOfDirectory(dirPath: string): string[];
declare function isDirectory(): boolean;
https://github.com/microsoft/TypeScript/issues/36048
Editor Improvements
The TypeScript compiler not only powers the TypeScript editing
experience in most major editors, it also powers the JavaScript
experience in the Visual Studio family of editors and more. Using new
TypeScript/JavaScript functionality in your editor will differ depending
on your editor, but
CommonJS Auto-Imports in
JavaScript
One great new improvement is in auto-imports in JavaScript files using
CommonJS modules.
const fs = require("fs");
TypeScript now automatically detects the types of imports you're using
to keep your file's style clean and consistent.
For more details on the change, see the corresponding pull request.
/*start*/
for (let i = 0; i <= maxValue; i++) {
// First get the squared value.
let square = i ** 2;
printSquares();
function printSquares() {
for (let i = 0; i <= maxValue; i++) {
// First get the squared value.
let square = i ** 2;
// Now print the squared value.
console.log(square);
}
}
That's not ideal - we had a blank line between each statement in our
for loop, but the refactoring got rid of it! TypeScript 3.9 does a
little more work to preserve what we write.
const maxValue = 100;
printSquares();
function printSquares() {
for (let i = 0; i <= maxValue; i++) {
// First get the squared value.
let square = i ** 2;
You can see more about the implementation in this pull request
One case where this slightly fell over is when a tsconfig.json simply
existed to reference other tsconfig.json files.
// tsconfig.json
{
files: [],
references: [
{ path: "./tsconfig.shared.json" },
{ path: "./tsconfig.frontend.json" },
{ path: "./tsconfig.backend.json" },
],
}
This file that really does nothing but manage other project files is
often called a "solution" in some environments. Here, none of these
tsconfig.*.json files get picked up by the server, but we'd really like
the language server to understand that the current .ts file probably
belongs to one of the mentioned projects in this root tsconfig.json .
foo?.bar!.baz;
(foo?.bar).baz;
The Babel team who pointed this behavior out, and most users who
provided feedback to us, believe that this behavior is wrong. We do
too! The thing we heard the most was that the ! operator should
just "disappear" since the intent was to remove null and undefined
In other words, most people felt that the original snippet should be
interpreted as
foo?.bar.baz;
which just evaluates to undefined when foo is undefined .
This is a breaking change, but we believe most code was written with
the new interpretation in mind. Users who want to revert to the old
behavior can add explicit parentheses around the left side of the !
operator.
foo?.bar!.baz;
Luckily, thanks to the pull request enforcing this from Brad Zacher,
you'll get an error message along the lines of
For example:
let directions = <span>Navigate to: Menu Bar > Tools > Options</div>
// ~ ~
// Unexpected token. Did you mean `{'>'}` or `>`?
That error message came with a handy quick fix, and thanks to
Alexander Tarasyuk, you can apply these changes in bulk if you have a
lot of errors.
t t t t
Stricter Checks on Intersections
and Optional Properties
Generally, an intersection type like A & B is assignable to C if either
A or B is assignable to C ; however, sometimes that has problems
with optional properties. For example, take the following:
interface A {
a: number; // notice this is 'number'
}
interface B {
b: string;
}
interface C {
a?: boolean; // notice this is 'boolean'
b: string;
}
y = x;
t t d d
Intersections Reduced By
Discriminant Properties
There are a few cases where you might end up with types that
describe values that just don't exist. For example
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
Thanks to a pull request from GitHub user pathurs, TypeScript 3.9 now
conforms more closely with ECMAScript in this regard.
l t d
export * is Always Retained
In previous TypeScript versions, declarations like export * from "foo"
Adding this file to an ambient *.d.ts to your project will bring them
back:
interface AudioTrackList {
[Symbol.iterator](): IterableIterator<AudioTrack>;
}
interface HTMLVideoElement {
readonly audioTracks: AudioTrackList
msHorizontalMirror: boolean;
readonly msIsLayoutOptimalForPlayback: boolean;
readonly msIsStereo3D: boolean;
msStereo3DPackingMode: string;
msStereo3DRenderMode: string;
msZoom: boolean;
onMSVideoFormatChanged: ((this: HTMLVideoElement, ev: Event) => any) | null;
onMSVideoFrameStepCompleted: ((this: HTMLVideoElement, ev: Event) => any) | null;
onMSVideoOptimalLayoutChanged: ((this: HTMLVideoElement, ev: Event) => any) |
null;
webkitDisplayingFullscreen: boolean;
webkitSupportsFullscreen: boolean;
}
interface MediaError {
readonly msExtendedCode: number;
readonly MS_MEDIA_ERR_ENCRYPTED: number;
}
Type-Only Imports and
Export
This feature is something most users may never have to think about;
however, if you've hit issues under --isolatedModules , TypeScript's
transpileModule API, or Babel, this feature might be relevant.
TypeScript 3.8 adds a new syntax for type-only imports and exports.
It's important to note that classes have a value at runtime and a type
at design-time, and the use is context-sensitive. When using import
type to import a class, you can't do things like extend from it.
interface ButtonProps {
// ...
}
option), but will error when a value import is only used as a type.
This might be useful if you want to ensure no values are being
accidentally imported, but still make side-effect imports explicit.
For more information about the feature, you can take a look at the
pull request, and relevant changes around broadening where imports
from an import type declaration can be used.
ECMAScript Private Fields
TypeScript 3.8 brings support for ECMAScript's private fields, part of
the stage-3 class fields proposal.
class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
greet() {
console.log(`Hello, my name is ${this.#name}!`);
}
}
jeremy.#name;
// ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.
class C {
foo = 10;
cHelper() {
return this.foo;
}
}
class D extends C {
foo = 20;
dHelper() {
return this.foo;
}
}
With private fields, you'll never have to worry about this, since each
field name is unique to the containing class.
class C {
#foo = 10;
cHelper() {
return this.#foo;
}
}
class D extends C {
#foo = 20;
dHelper() {
return this.#foo;
}
}
class Square {
#sideLength: number;
constructor(sideLength: number) {
this.#sideLength = sideLength;
}
equals(other: any) {
return this.#sideLength === other.#sideLength;
}
}
// Boom!
// TypeError: attempted to get private field on non-instance
// This fails because 'b' is not an instance of 'Square'.
console.log(a.equals(b));
Finally, for any plain .js file users, private fields always have to be
declared before they're assigned to.
class C {
// No declaration for '#foo'
// :(
constructor(foo: number) {
// SyntaxError!
// '#foo' needs to be declared before writing to it.
this.#foo = foo;
}
}
class C {
/** @type {number} */
#foo;
constructor(foo: number) {
// This works.
this.#foo = foo;
}
}
For more information about the implementation, you can check out the
original pull request
class C {
private foo = 10;
}
The upside is that this sort of "soft privacy" can help your consumers
temporarily work around not having access to some API, and also works
in any runtime.
class C {
#foo = 10;
}
One more thing to think about is where you intend for your code to
run. TypeScript currently can't support this feature unless targeting
ECMAScript 2015 (ES6) targets or higher. This is because our
downleveled implementation uses WeakMap s to enforce privacy, and
WeakMap s can't be polyfilled in a way that doesn't cause memory leaks.
In contrast, TypeScript's private -declared properties work with all
targets - even ECMAScript 3!
Note there's a subtlety: top-level await only works at the top level
of a module, and files are only considered modules when TypeScript
finds an import or an export . In some basic cases, you might need to
write out export {} as some boilerplate to make sure of this.
Top level await may not work in all environments where you might
expect at this point. Currently, you can only use top level await when
the target compiler option is es2017 or above, and module is esnext
or system . Support within several environments and bundlers may be
limited or may require enabling experimental support.
For more information on our implementation, you can check out the
original pull request.
es2020 for target and
module
TypeScript 3.8 supports es2020 as an option for module and target .
This will preserve newer ECMAScript 2020 features like optional
chaining, nullish coalescing, export * as ns , and dynamic import(...)
syntax. It also means bigint literals now have a stable target below
esnext .
JSDoc Property Modifiers
TypeScript 3.8 supports JavaScript files by turning on the allowJs
flag, and also supports type-checking those JavaScript files via the
checkJs option or by adding a // @ts-check comment to the top of
your .js files.
// @ts-check
class Foo {
constructor() {
/** @private */
this.stuff = 100;
}
printStuff() {
console.log(this.stuff);
}
}
new Foo().stuff;
// ~~~~~
// error! Property 'stuff' is private and only accessible within class 'Foo'.
@public is always implied and can be left off, but means that a
property can be reached from anywhere.
@private means that a property can only be used within the
containing class.
@protected means that a property can only be used within the
containing class, and all derived subclasses, but not on dissimilar
instances of the containing class.
// @ts-check
class Foo {
constructor() {
/** @readonly */
this.stuff = 100;
}
writeToStuff() {
this.stuff = 200;
// ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.
}
}
new Foo().stuff++;
// ~~~~~
// Cannot assign to 'stuff' because it is a read-only property.
Better Directory Watching on
Linux and watchOptions
TypeScript 3.8 ships a new strategy for watching directories, which is
crucial for efficiently picking up changes to node_modules .
watchFile : the strategy for how individual files are watched. This
can be set to
In a codebase like Visual Studio Code, this reduced rebuild times for
changes in certain files from about 14 seconds to about 1 second. While
we don't necessarily recommend this option for all codebases, you
might be interested if you have an extremely large codebase and are
willing to defer full project errors until later (e.g. a dedicated build
via a tsconfig.fullbuild.json or in CI).
For more details, you can see the original pull request.
Optional Chaining
Playground
Optional chaining is issue #16 on our issue tracker. For context, there
have been over 23,000 issues on the TypeScript issue tracker since
then.
At its core, optional chaining lets us write code where TypeScript can
immediately stop running some expressions if we run into a null or
undefined . The star of the show in optional chaining is the new ?.
let x = foo?.bar.baz();
More plainly, that code snippet is the same as writing the following.
Note that if bar is null or undefined , our code will still hit an error
accessing baz . Likewise, if baz is null or undefined , we'll hit an
error at the call site. ?. only checks for whether the value on the
left of it is null or undefined - not any of the subsequent
properties.
// After-ish
if (foo?.bar?.baz) {
// ...
}
Keep in mind that ?. acts differently than those && operations since
&& will act specially on "falsy" values (e.g. the empty string, 0 ,
NaN , and, well, false ), but this is an intentional feature of the
construct. It doesn't short-circuit on valid data like 0 or empty
strings.
Optional chaining also includes two other operations. First there's the
optional element access which acts similarly to optional property
accesses, but allows us to access non-identifier properties (e.g.
arbitrary strings, numbers, and symbols):
/**
* Get the first element of the array if we have an array.
* Otherwise return undefined.
*/
function tryGetFirstElement<T>(arr?: T[]) {
return arr?.[0];
// equivalent to
// return (arr === null || arr === undefined) ?
// undefined :
// arr[0];
}
return result;
}
let temp = foo === null || foo === undefined ? undefined : foo.bar;
More more details, you can read up on the proposal and view the
original pull request.
Nullish Coalescing
Playground
this is a new way to say that the value foo will be used when it's
"present"; but when it's null or undefined , calculate bar() in its
place.
let x = foo !== null && foo !== undefined ? foo : bar();
function initializeAudio() {
let volume = localStorage.volume || 0.5;
// ...
}
When localStorage.volume is set to 0 , the page will set the volume to
0.5 which is unintended. ?? avoids some unintended behavior from
0 , NaN and "" being treated as falsy values.
function multiply(x, y) {
assert(typeof x === "number");
assert(typeof y === "number");
return x * y;
}
function yell(str) {
assert(typeof str === "string");
return str.toUppercase();
// Oops! We misspelled 'toUpperCase'.
// Would be great if TypeScript still caught this!
}
The alternative was to instead rewrite the code so that the language
could analyze it, but this isn't convenient.
function yell(str) {
if (typeof str !== "string") {
throw new TypeError("str should have been a string.");
}
// Error caught!
return str.toUppercase();
}
The first type of assertion signature models the way that Node's
assert function works. It ensures that whatever condition is being
checked must be true for the remainder of the containing scope.
asserts condition says that whatever gets passed into the condition
return str.toUppercase();
// ~~~~~~~~~~~
// error: Property 'toUppercase' does not exist on type 'string'.
// Did you mean 'toUpperCase'?
}
Here asserts val is string ensures that after any call to assertIsString ,
any variable passed in will be known to be a string .
return str.toUppercase();
// ~~~~~~~~~~~
// error: Property 'toUppercase' does not exist on type 'string'.
// Did you mean 'toUpperCase'?
}
And just like type predicate signatures, these assertion signatures are
incredibly expressive. We can express some fairly sophisticated ideas
with these.
To read up more about assertion signatures, check out the original pull
request.
Better Support for never -
Returning Functions
As part of the work for assertion signatures, TypeScript needed to
encode more about where and which functions were being called. This
gave us the opportunity to expand support for another class of
functions: functions that return never .
As with assertion functions, you can read up more at the same pull
request.
(More) Recursive Type Aliases
Playground
This is fairly consistent with how other languages treat type aliases,
but it does give rise to some slightly surprising scenarios for how
users leverage the feature. For example, in TypeScript 3.6 and prior,
the following causes an error.
This is strange because there is technically nothing wrong with any use
users could always write what was effectively the same code by
introducing an interface.
This means that code like the following that was trying to represent
JSON…
interface JsonObject {
[property: string]: Json;
}
type Json =
| string
| number
| boolean
| null
| { [property: string]: Json }
| Json[];
For more information, you can read up on the original pull request.
--declaration and --allowJs
The --declaration flag in TypeScript allows us to generate .d.ts files
(declaration files) from TypeScript source files (i.e. .ts and .tsx
The most impactful outcome of this feature might a bit subtle: with
TypeScript 3.7, users can write libraries in JSDoc annotated JavaScript
and support TypeScript users.
The way that this works is that when using allowJs , TypeScript has
some best-effort analyses to understand common JavaScript patterns;
however, the way that some patterns are expressed in JavaScript don't
necessarily look like their equivalents in TypeScript. When declaration
emit is turned on, TypeScript figures out the best way to transform
JSDoc comments and CommonJS exports into valid type declarations
and the like in the output .d.ts files.
module.exports.blurImage = blurImage;
/**
* Produces a blurred image from an input buffer.
*
* @param input {Uint8Array}
* @param width {number}
* @param height {number}
*/
function blurImage(input, width, height) {
const numPixels = width * height * 4;
assert(input.length === numPixels);
const result = new Uint8Array(numPixels);
// TODO
return result;
}
/**
* Produces a blurred image from an input buffer.
*
* @param input {Uint8Array}
* @param width {number}
* @param height {number}
*/
export function blurImage(
input: Uint8Array,
width: number,
height: number
): Uint8Array;
This can go beyond basic functions with @param tags too, where the
following example:
/**
* @callback Job
* @returns {void}
*/
For more details, you can check out the original pull request.
The useDefineForClassFields
Flag and The declare
Property Modifier
Back when TypeScript implemented public class fields, we assumed to
the best of our abilities that the following code
class C {
foo = 100;
bar: string;
}
class C {
constructor() {
this.foo = 100;
}
}
While TypeScript 3.7 isn't changing any existing emit by default, we've
been rolling out changes incrementally to help users mitigate potential
future breakage. We've provided a new flag called
useDefineForClassFields to enable this emit mode with some new checking
logic.
This can cause quite a bit of fallout for existing code that use
inheritance. First of all, set accessors from base classes won't get
triggered - they'll be completely overwritten.
class Base {
set data(value: string) {
console.log("data changed to " + value);
}
}
interface Animal {
animalStuff: any;
}
interface Dog extends Animal {
dogStuff: any;
}
class AnimalHouse {
resident: Animal;
constructor(animal: Animal) {
this.resident = animal;
}
}
constructor(dog: Dog) {
super(dog);
}
}
What these two boil down to is that mixing properties with accessors is
going to cause issues, and so will re-declaring properties with no
initializers.
To detect the issue around accessors, TypeScript 3.7 will now emit
get / set accessors in .d.ts files so that in TypeScript can check for
overridden accessors.
Code that's impacted by the class fields change can get around the
issue by converting field initializers to assignments in constructor
bodies.
class Base {
set data(value: string) {
console.log("data changed to " + value);
}
}
To help mitigate the second issue, you can either add an explicit
initializer or add a declare modifier to indicate that a property should
have no emit.
interface Animal {
animalStuff: any;
}
interface Dog extends Animal {
dogStuff: any;
}
class AnimalHouse {
resident: Animal;
constructor(animal: Animal) {
this.resident = animal;
}
}
constructor(dog: Dog) {
super(dog);
}
}
For more information, you can take a look at the original pull request
for these changes.
You can read up more about this change by reading up on its pull
request.
Uncalled Function Checks
A common and dangerous error is to forget to invoke a function,
especially if the function has zero arguments or is named in a way that
implies it might be a property rather than a function.
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
// later...
interface User {
isAdministrator(): boolean;
notify(): void;
doNotDisturb?(): boolean;
}
If you intended to test the function without calling it, you can
correct the definition of it to include undefined / null , or use !! to
write something like if (!!user.isAdministrator) to indicate that the
coercion is intentional.
We owe a big thanks to GitHub user @jwbay who took the initiative to
create a proof-of-concept and iterated to provide us with the
current version.
// @ts-nocheck in TypeScript
Files
TypeScript 3.7 allows us to add // @ts-nocheck comments to the top of
TypeScript files to disable semantic checks. Historically this comment was
only respected in JavaScript source files in the presence of checkJs ,
but we've expanded support to TypeScript files to make migrations
easier for all users.
Semicolon Formatter Option
TypeScript's built-in formatter now supports semicolon insertion and
removal at locations where a trailing semicolon is optional due to
JavaScript's automatic semicolon insertion (ASI) rules. The setting is
available now in Visual Studio Code Insiders, and will be available in
Visual Studio 16.4 Preview 2 in the Tools Options menu.
DOM Changes
Types in lib.dom.d.ts have been updated. These changes are largely
correctness changes related to nullability, but impact will ultimately
depend on your codebase.
// ./someOtherModule.ts
interface SomeType {
y: string;
}
// ./myModule.ts
import { SomeType } from "./someOtherModule";
export interface SomeType {
x: number;
}
function* foo() {
if (Math.random() < 0.5) yield 100;
return "Finished!";
}
function* bar() {
let x: { hello(): void } = yield;
x.hello();
}
In TypeScript 3.6, the checker now knows that the correct type for
curr.value should be string in our first example, and will correctly
error on our call to next() in our last example. This is thanks to
some changes in the Iterator and IteratorResult type declarations to
include a few new type parameters, and to a new type that TypeScript
uses to represent generators called the Generator type.
The Iterator type now allows users to specify the yielded type, the
returned type, and the type that next can accept.
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
function* foo() {
let x: string = yield;
console.log(x.toUpperCase());
}
let x = foo();
x.next(); // first call to 'next' is always ignored
x.next(42); // error! 'number' is not assignable to 'string'
// prints:
//
// 0
// 1
// 2
// 3
// 4
// 5
// DONE!
For more details on the change, see the pull request here.
More Accurate Array Spread
In pre-ES2015 targets, the most faithful emit for constructs like
for / of loops and array spreads can be a bit heavy. For this reason,
TypeScript uses a simpler emit by default that only supports array
types, and supports iterating on other types using the --
works fairly well; however, there were some common cases where the
transformation of array spreads had observable differences. For
example, the following array containing a spread
[...Array(5)];
However, TypeScript would instead transform the original code into this
code:
Array(5).slice();
interface User {
name: string;
age: number;
location: string;
}
// This module:
console.log(import.meta.url);
accessors.
Ambient Classes and Functions
Can Merge
In previous versions of TypeScript, it was an error to merge classes
and functions under any circumstances. Now, ambient classes and
functions (classes/functions with the declare modifier, or in .d.ts
files) can merge. This means that now you can write the following:
For more details on these APIs, you can see the original pull request.
Semicolon-Aware Code Edits
Editors like Visual Studio and Visual Studio Code can automatically apply
quick fixes, refactorings, and other transformations like automatically
importing values from other modules. These transformations are
powered by TypeScript, and older versions of TypeScript
unconditionally added semicolons to the end of every statement;
unfortunately, this disagreed with many users' style guidelines, and
many users were displeased with the editor inserting semicolons.
Type-checking speed-ups
TypeScript 3.5 contains certain optimizations over TypeScript 3.4 for
type-checking more efficiently. These improvements are significantly
more pronounced in editor scenarios where type-checking drives
operations like code completion lists.
--incremental improvements
TypeScript 3.5 improves on 3.4's --incremental build mode, by saving
information about how the state of the world was calculated -
compiler settings, why files were looked up, where files were found,
etc. In scenarios involving hundreds of projects using TypeScript's
project references in --build mode, we've found that the amount of
time rebuilding can be reduced by as much as 68% compared to
TypeScript 3.4!
type Person = {
name: string;
age: number;
location: string;
};
// equivalent to
type QuantumPerson = {
name: string;
age: number;
};
Here we were able to copy over all the properties of Person except
for location using the Omit helper.
For more details, see the pull request on GitHub to add Omit , as well
as the change to use Omit for object rest.
type Label = {
name: string;
};
flag.
This mode adds flexibility for mixing and matching the way 3rd party
libraries, where globals that libraries declare can always be consumed,
even from within modules.
target = source;
interface Bar {
kind: "bar";
value: number;
}
However, this was a bit overly strict for the original example. If you
figure out the precise type of any possible value of S , you can
actually see that it matches the types in T exactly.
For more details, you can see the original pull request on GitHub.
Higher order type inference
from generic constructors
In TypeScript 3.4, we improved inference for when generic functions
that return functions like so:
function compose<T, U, V>(f: (x: T) => U, g: (y: U) => V): (x: T) => V {
return x => g(f(x));
}
Instead of a relatively useless type like (x: {}) => Box<{}[]> , which older
versions of the language would infer, TypeScript 3.4's inference allows
newFn to be generic. Its new type is <T>(x: T) => Box<T[]> .
class Bag<U> {
kind: "bag";
value: U;
constructor(value: U) {
this.value = value;
}
}
// tsconfig.json
{
compilerOptions: {
incremental: true,
outDir: "./lib"
},
include: ["./src"]
}
By default with these settings, when we run tsc , TypeScript will look
for a file called .tsbuildinfo in the output directory ( ./lib ). If
./lib/.tsbuildinfo doesn't exist, it'll be generated. But if it does,
tsc will try to use that file to incrementally type-check and update
our output files.
These .tsbuildinfo files can be safely deleted and don't have any
impact on our code at runtime - they're purely used to make
compilations faster. We can also name them anything that we want, and
place them anywhere we want using the --tsBuildInfoFile flag.
// front-end.tsconfig.json
{
compilerOptions: {
incremental: true,
tsBuildInfoFile: "./buildcache/front-end",
outDir: "./lib"
},
include: ["./src"]
}
Composite projects
Part of the intent with composite projects ( tsconfig.json s with
composite set to true ) is that references between different projects
can be built incrementally. As such, composite projects will always
produce .tsbuildinfo files.
outFile
When outFile is used, the build information file's name will be based
on the output file's name. As an example, if our output JavaScript file
is ./output/foo.js , then under the --incremental flag, TypeScript will
generate the file ./output/foo.tsbuildinfo . As above, this can be
controlled with the --tsBuildInfoFile flag.
Higher order type inference
from generic functions
TypeScript 3.4 can now produce generic function types when inference
from other generic functions produces free type variables for
inferences. This means many function composition patterns now work
better in 3.4.
To get more specific, let's build up some motivation and consider the
following compose function:
function compose<A, B, C>(f: (arg: A) => B, g: (arg: B) => C): (arg: A) => C {
return x => g(f(x));
}
and then g .
When calling this function, TypeScript will try to figure out the types
of A , B , and C through a process called type argument inference.
This inference process usually works pretty well:
interface Person {
name: string;
age: number;
}
interface Box<T> {
value: T;
}
makeBoxedArray("hello!").value[0].toUpperCase();
// ~~~~~~~~~~~
// error: Property 'toUpperCase' does not exist on type '{}'.
In older versions, TypeScript would infer the empty object type ( {} )
when inferring from other type variables like T and U .
Notice that T has been propagated from makeArray into the resulting
type's type parameter list. This means that genericity from compose 's
arguments has been preserved and our makeBoxedArray sample will just
work!
interface Box<T> {
value: T;
}
For more details, you can read more at the original change.
Improvements for
ReadonlyArray and readonly
tuples
TypeScript 3.4 makes it a little bit easier to use read-only array-like
types.
readonly tuples
TypeScript 3.4 also introduces new support for readonly tuples. We
can prefix any tuple type with the readonly keyword to make it a
readonly tuple, much like we now can with array shorthand syntax. As
you might expect, unlike ordinary tuples whose slots could be written
to, readonly tuples only permit reading from those positions.
The same way that ordinary tuples are types that extend from Array
type Boxify<T> = {
[K in keyof T]: Box<T[K]>;
};
// { a: Box<string>, b: Box<number> }
type A = Boxify<{ a: string; b: number }>;
// Array<Box<number>>
type B = Boxify<number[]>;
// [Box<string>, Box<number>]
type C = Boxify<[string, boolean]>;
// lib.d.ts
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// number[]
type B = Readonly<number[]>;
// [string, boolean]
type C = Readonly<[string, boolean]>;
counterparts.
// How code acts now *with* TypeScript 3.4
// readonly number[]
type B = Readonly<number[]>;
Similarly, you could write a utility type like Writable mapped type that
strips away readonly -ness, and that would convert readonly array
containers back to their mutable equivalents.
type Writable<T> = {
-readonly [K in keyof T]: T[K];
};
// { a: string, b: number }
type A = Writable<{
readonly a: string;
readonly b: number;
}>;
// number[]
type B = Writable<readonly number[]>;
// [string, boolean]
type C = Writable<readonly [string, boolean]>;
Caveats
Despite its appearance, the readonly type modifier can only be used
for syntax on array types and tuple types. It is not a general-
purpose type operator.
let err1: readonly Set<number>; // error!
let err2: readonly Array<boolean>; // error!
// Type '"hello"'
let x = "hello" as const;
Outside of .tsx files, the angle bracket assertion syntax can also be
used.
// Type '"hello"'
let x = <const>"hello";
This feature means that types that would otherwise be used just to
hint immutability to the compiler can often be omitted.
// Works with no types referenced or declared.
// We only needed a single const assertion.
function getShapes() {
let result = [
{ kind: "circle", radius: 100 },
{ kind: "square", sideLength: 50 }
] as const;
return result;
}
This can even be used to enable enum -like patterns in plain JavaScript
code if you choose not to use TypeScript's enum construct.
export default {
red: "RED",
blue: "BLUE",
green: "GREEN"
} as const;
Caveats
One thing to note is that const assertions can only be applied
immediately on simple literal expressions.
// Works!
let b = Math.random() < 0.5 ? (0 as const) : (1 as const);
let foo = {
name: "foo",
contents: arr
} as const;
foo.contents.push(5); // ...works!
For more details, you can check out the respective pull request.
Type-checking for globalThis
TypeScript 3.4 introduces support for type-checking ECMAScript's new
globalThis - a global variable that, well, refers to the global scope.
Unlike the above solutions, globalThis provides a standard way for
accessing the global scope which can be used across different
environments.
// in a global file:
Note that global variables declared with let and const don't show up
on globalThis .
type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruit
type ColorConsumer = (color: Color) => string; // consumes and describes the colors
or a string .
type FruitEater = (fruit: Fruit) => number; // eats and ranks the fruit
type ColorConsumer = (color: Color) => string; // consumes and describes the colors
Color is really the same as ("apple" | "orange") & ("red" | "orange") which is
equivalent to ("apple" & "red") | ("apple" & "orange") | ("orange" & "red") |
On the other hand, methods like forEach will now be callable, but
under noImplicitAny there may be some issues.
interface Dog {
kind: "dog";
dogProp: any;
}
interface Cat {
kind: "cat";
catProp: any;
}
catOrDogArray.forEach(animal => {
// ~~~~~~ error!
// Parameter 'animal' implicitly has an 'any' type.
});
helper types from lib.d.ts , and using the generic type in question as
well as the names of the other bindings in the destructuring pattern.
fibonacci(10000n);
While you might imagine close interaction between number and bigint ,
the two are separate domains.
We'd like to extend a huge thanks to Caleb Sander for all the work on
this feature. We're grateful for the contribution, and we're sure our
users are too!
Caveats
As we mentioned, BigInt support is only available for the esnext
{
"extends": "@my-team/tsconfig-base",
"include": ["./**/*"]
"compilerOptions": {
// Override certain options on a project-by-project basis.
"strictBindCallApply": false,
}
}
field) and print that out. This can be useful for diagnosing
configuration issues in general.
Object.defineProperty
declarations in JavaScript
When writing in JavaScript files (using allowJs ), TypeScript now
recognizes declarations that use Object.defineProperty . This means you'll
get better completions, and stronger type-checking when enabling
type-checking in JavaScript files (by turning on the checkJs option or
adding a // @ts-check comment to the top of your file).
// @ts-check
obj.x.toLowercase();
// ~~~~~~~~~~~
// error:
// Property 'toLowercase' does not exist on type 'string'.
// Did you mean 'toLowerCase'?
obj.x = "world";
// ~
// error:
// Cannot assign to 'x' because it is a read-only property.
Mapped types on tuples and
arrays
[1]
In TypeScript 3.1, mapped object types over tuples and arrays now
produce new tuples/arrays, rather than creating a new type where
members like push() , pop() , and length are converted. For example:
Promise<number>] .
Properties declarations on
functions
TypeScript 3.1 brings the ability to define properties on function
declarations and const -declared functions, simply by assigning to
properties on these functions in the same scope. This allows us to
write canonical JavaScript code without resorting to namespace hacks.
For example:
function readImage(path: string, callback: (err: any, image: Image) => void) {
// ...
}
FooComponent.defaultProps = {
name: "(anonymous)"
};
[1]
[1] More specifically, homomorphic mapped types like in the above form.
Version selection with
typesVersions
Feedback from our community, as well as our own experience, has
shown us that leveraging the newest TypeScript features while also
accommodating users on the older versions are difficult. TypeScript
introduces a new feature called typesVersions to help accommodate
these scenarios.
{
"name": "package-name",
"version": "1.0",
"types": "./index.d.ts",
"typesVersions": {
">=3.1": { "*": ["ts3.1/*"] }
}
}
{
"name": "package-name",
"version": "1.0",
"types": "./index.d.ts",
"typesVersions": {
">=3.2": { "*": ["ts3.2/*"] },
">=3.1": { "*": ["ts3.1/*"] }
}
}
{
name: "package-name",
version: "1.0",
types: "./index.d.ts",
typesVersions: {
// NOTE: this doesn't work!
">=3.1": { "*": ["ts3.1/*"] },
">=3.2": { "*": ["ts3.2/*"] }
}
}
Project References
TypeScript 3.0 introduces a new concept of project references.
Project references allow TypeScript projects to depend on other
TypeScript projects - specifically, allowing tsconfig.json files to
reference other tsconfig.json files. Specifying these dependencies
makes it easier to split your code into smaller projects, since it gives
TypeScript (and tools around it) a way to understand build ordering
and output structure.
TypeScript 3.0 also introduces a new mode for tsc, the --build flag,
that works hand-in-hand with project references to enable faster
TypeScript builds.
Example
Example
modifier on its type and all elements to the right of it also have ?
modifiers.
When tuple types are inferred for rest parameters, optional parameters
in the source become optional tuple elements in the inferred type.
boolean?] is 1 | 2 | 3 .
elements.
Example
The type of the length property of a tuple type with a rest element
is number .
New unknown top type
TypeScript 3.0 introduces a new top type unknown . unknown is the
type-safe counterpart of any . Anything is assignable to unknown , but
unknown isn't assignable to anything but itself and any without a type
assertion or a control flow based narrowing. Likewise, no operations
are permitted on an unknown without first asserting or narrowing to a
more specific type.
Example
// In an intersection everything absorbs unknown
// keyof unknown
function f25() {
let x: unknown;
let y = x;
}
class C1 {
a: string; // Error
b: unknown;
c: any;
}
Support for defaultProps in
JSX
TypeScript 2.9 and earlier didn’t leverage React defaultProps
TypeScript 3.0 adds support for a new type alias in the JSX
Changes to @types/React
Built-in lib files are referenced in the same fashion as the "lib"
For declaration file authors who relay on built-in types, e.g. DOM APIs
or built-in JS run-time constructors like Symbol or Iterable , triple-
slash-reference lib directives are the recommended. Previously these
.d.ts files had to add forward/duplicate declarations of such types.
Example
"foo".padStart(4);
Support number and symbol
named properties with keyof
and mapped types
TypeScript 2.9 adds support for number and symbol named properties
in index types and mapped types. Previously, the keyof operator and
mapped types only supported string named properties.
Changes include:
number | symbol .
A mapped type { [P in K]: XXX } permits any K assignable to
string | number | symbol .
In a for...in statement for an object of a generic type T , the
inferred type of the iteration variable was previously keyof T but
is now Extract<keyof T, string> . (In other words, the subset of
keyof T that includes only string-like values.)
Where:
Example
const c = "c";
const d = 10;
const e = Symbol();
const enum E1 {
A,
B,
C,
}
const enum E2 {
A = "A",
B = "B",
C = "C",
}
type Foo = {
a: string; // String-like name
5: string; // Number-like name
[c]: string; // String-like name
[d]: string; // Number-like name
[e]: string; // Symbol-like name
[E1.A]: string; // Number-like name
[E2.A]: string; // String-like name
};
const enumToStringMap = {
[Enum.A]: "Name A",
[Enum.B]: "Name B",
[Enum.C]: "Name C",
};
const symbolToNumberMap = {
[sym1]: 1,
[sym2]: 2,
[sym3]: 3,
};
Example
d t
Recommendations
If your functions are only able to handle string named property
keys, use Extract<keyof T, string> in the declaration:
If your functions are open to handling all property keys, then the
changes should be done down-stream:
Example
Example
interface MyProps {
name: string;
age: number;
}
styledComponent<MyProps>`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
// inference fails because 'number' and 'string' are both candidates that conflict
let a = tag<string | number>`${100} ${"hello"}`;
import types
Modules can import types declared in other modules. But non-module
global scripts cannot access types declared in modules. Enter import
types.
Example
// module.d.ts
// global-script.ts
// a.js
/**
* @param p { import("./module").Pet }
*/
function walk(p) {
console.log(`Walking ${p.name}...`);
}
Relaxing declaration emit
visiblity rules
With import types available, many of the visibility errors reported
during declaration file generation can be handled by the compiler
without the need to change the input.
For instance:
With TypeScript 2.9, no errors are reported, and now the generated
file looks like:
Example
// node.d.ts
interface ImportMeta {
__dirname: string;
}
Example
// settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
// a.ts
Use --pretty false on the command line or set "pretty": false in your
tsconfig.json to disable --pretty output.
New --declarationMap
Enabling --declarationMap alongside --declaration causes the compiler to
emit .d.ts.map files alongside the output .d.ts files. Language
Services can also now understand these map files, and uses them to
map declaration-file based definition locations to their original source,
when available.
T extends U ? X : Y
First, given types T' and U' that are instantiations of T and
U where all occurrences of type parameters are replaced with
any , if T' is not assignable to U' , the conditional type is
resolved to Y . Intuitively, if the most permissive instantiation of
T is not assignable to the most permissive instantiation of U ,
we know that no instantiation will be and we can just resolve to
Y .
Next, for each type variable introduced by an infer (more later)
declaration within U collect a set of candidate types by inferring
from T to U (using the same inference algorithm as type
inference for generic functions). For a given infer type variable
V , if any candidates were inferred from co-variant positions,
the type inferred for V is a union of those candidates.
Otherwise, if any candidates were inferred from contra-variant
positions, the type inferred for V is an intersection of those
candidates. Otherwise, the type inferred for V is never .
Then, given a type T'' that is an instantiation of T where all
infer type variables are replaced with the types inferred in the
previous step, if T'' is definitely assignable to U , the
conditional type is resolved to X . The definitely assignable
relation is the same as the regular assignable relation, except that
type variable constraints are not considered. Intuitively, when a
type is definitely assignable to another type, we know that it will
be assignable for all instantiations of those types.
Otherwise, the condition depends on one or more type variables
and the conditional type is deferred.
Example
| (C extends U ? X : Y) .
Example
within X ).
Example
type Diff<T, U> = T extends U ? never : T; // Remove types from T that are
assignable to U
type Filter<T, U> = T extends U ? T : never; // Remove types from T that are not
assignable to U
type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T32 = Diff<string | number | (() => void), Function>; // string | number
type T33 = Filter<string | number | (() => void), Function>; // () => void
type NonNullable<T> = Diff<T, null | undefined>; // Remove null and undefined from
T
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
interface Part {
id: number;
name: string;
subparts: Part[];
updatePart(newName: string): void;
}
Example
type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
type T20 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void; b: (x: number) => void }>; // string &
number
When inferring from a type with multiple call signatures (such as the
type of an overloaded function), inferences are made from the last
signature (which, presumably, is the most permissive catch-all case). It
is not possible to perform overload resolution based on a list of
argument types.
type ReturnType<T extends (...args: any[]) => infer R> = R; // Error, not supported
However, much the same effect can be obtained by erasing the type
variables in the constraint and instead specifying a conditional type:
Example
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T02 = Exclude<string | number | (() => void), Function>; // string | number
type T03 = Extract<string | number | (() => void), Function>; // () => void
class C {
x = 0;
y = 0;
}
T, K>> .
Improved control over
mapped type modifiers
Mapped types support adding a readonly or ? modifier to a mapped
property, but they did not provide support the ability to remove
modifiers. This matters in homomorphic mapped types which by default
preserve the modifiers of the underlying type.
TypeScript 2.8 adds the ability for a mapped type to either add or
remove a particular modifier. Specifically, a readonly or ? property
modifier in a mapped type can now be prefixed with either + or -
Example
type MutableRequired<T> = { -readonly [P in keyof T]-?: T[P] }; // Remove readonly
and ?
type ReadonlyPartial<T> = { +readonly [P in keyof T]+?: T[P] }; // Add readonly and
?
Using this ability, lib.d.ts now has a new Required<T> type. This type
strips ? modifiers from all properties of T , thus making all
properties required.
Example
Example
Example
type A = { a: string };
type B = { b: string };
var C = (function() {
function C(n) {
this.p = n;
}
return C;
})();
C.staticProperty = 1;
Defaulted declarations
"Defaulted declarations" allow initializers that reference the declared
name in the left side of a logical or:
my = window.my || {};
my.app = my.app || {};
Prototype assignment
You can assign an object literal directly to the prototype property.
Individual prototype assignments still work too:
var C = function(p) {
this.p = p;
};
C.prototype = {
m() {
console.log(this.p);
}
};
C.prototype.q = function(r) {
return this.p === r;
};
Nested and merged
declarations
Nesting works to any level now, and merges correctly across files.
Previously neither was the case.
Example
Generates:
Example
// Lib
export const SERIALIZE = Symbol("serialize-method-key");
// consumer
Example
let x = {
[Foo]: 100,
[Bar]: "hello"
};
Example
// Works
declare const Foo: unique symbol;
// Also works.
class C {
static readonly StaticSymbol: unique symbol = Symbol();
}
Example
const Foo = Symbol();
const Bar = Symbol();
class C {
foo: number;
bar = "hello";
baz: boolean;
// ~~~
// Error! Property 'baz' has no initializer and is not definitely assigned in
the
// constructor.
constructor() {
this.foo = 42;
}
}
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
Example
let x: number;
initialize();
console.log(x + x);
// ~ ~
// Error! Variable 'x' is used before being assigned.
function initialize() {
x = 10;
}
// No error!
console.log(x + x);
function initialize() {
x = 10;
}
// No error!
console.log(x! + x!);
function initialize() {
x = 10;
Note that this is a breaking change for some code. If you need to
resort to the original behavior in which tuples only enforce a minimum
length, you can use a similar declaration that does not explicitly define
a length property, falling back to number .
Consider:
const obj = test ? { text: "hello" } : {}; // { text: string } | { text?: undefined
}
const s = obj.text; // string | undefined
Previously type {} was inferred for obj and the second line
subsequently caused an error because obj would appear to have no
properties. That obviously wasn't ideal.
Example
Multiple object literal type inferences for the same type parameter are
similarly collapsed into a single normalized union type:
declare function f<T>(...items: T[]): T;
// let obj: { a: number, b: number } |
// { a: string, b?: undefined } |
// { a?: undefined, b?: undefined }
let obj = f({ a: 1, b: 2 }, { a: "abc" }, {});
obj.a; // string | number | undefined
obj.b; // number | undefined
Improved handling of
structurally identical classes
and instanceof expressions
TypeScript 2.7 improves the handling of structurally identical classes in
union types and instanceof expressions:
This means that union types and instanceof properly distinguish between
structurally identical classes.
Example
class A {}
class B extends A {}
class C extends A {}
class D extends A {
c: string;
}
class E extends D {}
let a1 = [new A(), new B(), new C(), new D(), new E()]; // A[]
let a2 = [new B(), new C(), new D(), new E()]; // (B | C | D)[] (previously B[])
function f1(x: B | C | D) {
if (x instanceof B) {
x; // B (previously B | D)
} else if (x instanceof C) {
x; // C
} else {
x; // D (previously never)
}
}
Type guards inferred from
in operator
The in operator now acts as a narrowing expression for types.
Example
interface A {
a: number;
}
interface B {
b: string;
}
function foo(x: A | B) {
if ("a" in x) {
return x.a;
}
return x.b;
}
Support for import d from
"cjs" from CommonJS
modules with --esModuleInterop
TypeScript 2.7 updates CommonJS/AMD/UMD module emit to synthesize
namespace records based on the presence of an __esModule indicator
under --esModuleInterop . The change brings the generated output from
TypeScript closer to that generated by Babel.
foo = require("foo") .Things are simple here, but they don't work out
if the primary object being imported is a primitive or a class or a
function. ECMAScript spec stipulates that a namespace record is a
plain object, and that a namespace import ( foo in the example
above) is not callable, though allowed by TypeScript
Similarly a default import (i.e. import d from "foo" ) for a
CommonJS/AMD/UMD module as equivalent to const d =
Example
Will generate:
"use strict";
var __importStar =
(this && this.__importStar) ||
function(mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var __importDefault =
(this && this.__importDefault) ||
function(mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
exports.__esModule = true;
var foo = __importStar(require("foo"));
var bar_1 = __importDefault(require("bar"));
Numeric separators
TypeScript 2.7 brings support for ES Numeric Separators. Numeric
literals can now be separated into segments using _ .
Example
Example
interface Comparer<T> {
compare: (a: T, b: T) => number;
}
By the way, note that whereas some languages (e.g. C# and Scala)
require variance annotations ( out / in or + / - ), variance emerges
naturally from the actual use of a type parameter within a generic type
due to TypeScript's structural type system.
Note
interface Comparer<T> {
compare(a: T, b: T): number;
}
Example
function id(x) {
return x;
}
var _a;
function templateObjectFactory() {
return id(
_a || (_a = __makeTemplateObject(["hello world"], ["hello world"]))
);
}
Example
c:\ts>tsc --v
Version 2.6.0-dev.20171003
../test/a.ts(1,5): error TS2322: Тип ""string"" не может быть назначен для типа
"number".
オプション:
-h, --help このメッセージを表⽰します。
--all コンパイラ オプションをすべて表⽰します。
-v, --version コンパイラのバージョンを表⽰します。
--init TypeScript プロジェクトを初期化して、
tsconfig.jsonファイルを作成します。
-p ファイルまたはディレクトリ, --project ファイルまたはディレクトリ 構成ファイル
か、'tsconfig.json' を含むフォルダーにパスが指定されたプロジェクトをコ
ンパイルします。
--pretty ⾊とコンテキストを使⽤してエラーとメッセージに
スタイルを適⽤します (試験的)。
-w, --watch ⼊⼒ファイルを監視します。
-t バージョン, --target バージョン ECMAScript のターゲット バージョンを
指定します: 'ES3' (既定)、'ES5'、'ES2015'、'ES2016'、'ES2017'、'ES
NEXT'。
-m 種類, --module 種類 モジュール コード⽣成を指定します:
'none'、'commonjs'、'amd'、'system'、'umd'、'es2015'、'ESNext'。
--lib コンパイルに含めるライブラリ ファイルを指定し
ます:
'es5' 'es6' 'es2015' 'es7' 'es2016'
'es2017' 'esnext' 'dom' 'dom.iterable' 'webworker' 'scripthost' 'es201
5.core' 'es2015.collection' 'es2015.generator' 'es2015.iterable' 'es2015.promise'
'es2015.proxy' 'es2015.reflect' 'es2015.symbol' 'es2015.symbol.wellkno
wn' 'es2016.array.include' 'es2017.object' 'es2017.sharedmemory' 'es2017.string'
'es2017.intl' 'esnext.asynciterable'
--allowJs javascript ファイルのコンパイルを許可しま
す。
--jsx 種類 JSX コード⽣成を指定します:
'preserve'、'react-native'、'react'。
-d, --declaration 対応する '.d.ts' ファイルを⽣成します。
--sourceMap 対応する '.map' ファイルを⽣成します。
--outFile ファイル 出⼒を連結して 1 つのファイルを⽣成します。
--outDir ディレクトリ ディレクトリへ出⼒構造をリダイレクトしま
す。
--removeComments コメントを出⼒しないでください。
--noEmit 出⼒しないでください。
--strict strict 型チェックのオプションをすべて有効にし
ます。
--noImplicitAny 暗黙的な 'any' 型を含む式と宣⾔に関するエラー
を発⽣させます。
--strictNullChecks 厳格な null チェックを有効にします。
--noImplicitThis 暗黙的な 'any' 型を持つ 'this' 式でエラーが
発⽣します。
--alwaysStrict 厳格モードで解析してソース ファイルごとに
"use strict" を⽣成します。
--noUnusedLocals 使⽤されていないローカルに関するエラーを報告し
ます。
--noUnusedParameters 使⽤されていないパラメーターに関するエラーを報
告します。
--noImplicitReturns 関数の⼀部のコード パスが値を返さない場合にエ
ラーを報告します。
--noFallthroughCasesInSwitch switch ステートメントに case のフォールス
ルーがある場合にエラーを報告します。
--types コンパイルに含む型宣⾔ファイル。
@<ファイル>
Suppress errors in .ts files
using '// @ts-ignore'
comments
TypeScript 2.6 support suppressing errors in .js files using // @ts-
Example
if (false) {
// @ts-ignore: Unreachable code error
console.log("hello");
}
Please note that this comment only suppresses the error reporting, and
we recommend you use this comments very sparingly.
Faster tsc --watch
TypeScript 2.6 brings a faster --watch implementation. The new
version optimizes code generation and checking for code bases using ES
modules. Changes detected in a module file will result in only
regenerating the changed module, and files that depend on it, instead
of the whole project. Projects with large number of files should reap
the most benefit from this change.
Example
class C {
private m: number;
constructor() {
this.m = 0;
}
}
Also functions that are only called within their own bodies are
considered unused.
Example
function f() {
f(); // Error: 'f' is declared but its value is never read
}
Optional catch clause
variables
Thanks to work done by @tinganho, TypeScript 2.5 implements a new
ECMAScript feature that allows users to omit the variable in catch
clauses. For example, when using JSON.parse you may need to wrap calls
to the function with a try / catch , but you may not end up using the
SyntaxError that gets thrown when input is erroneous.
As a nice bonus, this can also reduce the memory and runtime
footprint of the compiler and language service by avoiding loading
.d.ts files from duplicate packages.
The --preserveSymlinks
compiler flag
TypeScript 2.5 brings the preserveSymlinks flag, which parallels the
behavior of the --preserve-symlinks flag in Node.js. This flag also
exhibits the opposite behavior to Webpack's resolve.symlinks option
(i.e. setting TypeScript's preserveSymlinks to true parallels setting
Webpack's resolve.symlinks to false , and vice-versa).
This means that you can conditionally and lazily import other modules
and libraries. For example, here's an async function that only imports
a utility library when it's needed:
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {
return a => a.map(f);
}
In TypeScript 2.4, the function on the right side implicitly gains type
parameters, and y is inferred to have the type of that type-
parameter.
function f(a: A, b: B) {
a = b; // Error
b = a; // Ok
}
Strict contravariance for
callback parameters
TypeScript has always compared parameters in a bivariant way. There
are a number of reasons for this, but by-and-large this was not been
a huge issue for our users until we saw some of the adverse effects it
had with Promise s and Observable s.
interface Mappable<T> {
map<U>(f: (x: T) => U): Mappable<U>;
}
a = b;
b = a;
Prior to TypeScript 2.4, this example would succeed. When relating the
types of map , TypeScript would bidirectionally relate their parameters
(i.e. the type of f ). When relating each f , TypeScript would also
bidirectionally relate the type of those parameters.
When relating the type of map in TS 2.4, the language will check
whether each parameter is a callback type, and if so, it will ensure
that those parameters are checked in a contravariant manner with
respect to the current relation.
In other words, TypeScript now catches the above bug, which may be a
breaking change for some users, but will largely be helpful.
Weak Type Detection
TypeScript 2.4 introduces the concept of "weak types". Any type that
contains nothing but a set of all-optional properties is considered to
be weak. For example, this Options type is a weak type:
interface Options {
data?: string;
timeout?: number;
maxRetries?: number;
}
const opts = {
payload: "hello world!",
retryOnFail: true
};
// Error!
sendMessage(opts);
// No overlap between the type of 'opts' and 'Options' itself.
// Maybe we meant to use 'data'/'maxRetries' instead of 'payload'/'retryOnFail'.
Since this is a breaking change, you may need to know about the
workarounds which are the same as those for strict object literal
checks:
{} ).
3. Use a type assertion (i.e. opts as Options ).
Generators and Iteration for
ES5/ES3
First some ES2016 terminology:
Iterators
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
The Iterator protocol also defines the target of some of the ES2015
features like for..of and spread operator and the array rest in
destructuring assignmnets.
Generators
function* f() {
yield 1;
yield* [2, 3];
}
New --downlevelIteration
TypeScript 2.3 adds full support for generators and the Iterator
protocol for ES3 and ES5 targets with --downlevelIteration flag.
With --downlevelIteration , the compiler uses new type check and emit
behavior that attempts to call a [Symbol.iterator]() method on the
iterated object if it is found, and creates a synthetic array iterator
over the object if it is not.
Async iterators
interface AsyncIterator<T> {
next(value?: any): Promise<IteratorResult<T>>;
return?(value?: any): Promise<IteratorResult<T>>;
throw?(e?: any): Promise<IteratorResult<T>>;
}
Async Generators
Caveats
Symbol.for("Symbol.asyncIterator");
You also need to include esnext in your --lib option, to get the
AsyncIterator declaration if you do not already have it.
Finally, if your target is ES5 or ES3, you'll also need to set the
--downlevelIterators flag.
Generic parameter defaults
TypeScript 2.3 adds support for declaring defaults for generic type
parameters.
Example
--strictNullChecks
--noImplicitAny
--noImplicitThis
--alwaysStrict
In exact terms, the --strict option sets the default value for the
compiler options listed above. This means it is still possible to
individually control the options. For example,
section. Thus, new projects started with tsc --init will by default have
the highest level of type safety enabled.
Enhanced --init output
Along with setting --strict on by default, tsc --init has an enhanced
output. Default tsconfig.json files generated by tsc --init now include
a set of the common compiler options along with their descriptions
commented out. Just un-comment the configuration you like to set to
get the desired behavior; we hope the new output simplifies the setting
up new projects and keeps configuration files readable as projects
grow.
Errors in .js files with --
checkJs
By default the TypeScript compiler does not report any errors in .js
files including using --allowJs . With TypeScript 2.3 type-checking
errors can also be reported in .js files with --checkJs .
.js files are still checked to ensure that they only include standard
ECMAScript features; type annotations are only allowed in .ts files and
are flagged as errors in .js files. JSDoc comments can be used to
add some type information to your JavaScript code, see JSDoc
Support documentation for more details about the supported JSDoc
constructs.
class Point {
constructor(public x: number, public y: number) {}
}
class Person {
constructor(public name: string) {}
}
Mixin classes can constrain the types of classes they can mix into by
specifying a construct signature return type in the constraint for the
type parameter. For example, the following WithLocation function
implements a subclass factory that adds a getLocation method to any
class that satisfies the Point interface (i.e. that has x and y
interface Point {
x: number;
y: number;
}
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
Support for new.target
The new.target meta-property is new syntax introduced in ES2015.
When an instance of a constructor is created via new , the value of
new.target is set to be a reference to the constructor function initially
used to allocate the instance. If a function is called rather than
constructed via new , new.target is set to undefined .
Example
function f() {
var _newTarget = this && this instanceof f ? this.constructor : void 0;
if (_newTarget) {
/* called via 'new' */
}
}
Better checking for
null / undefined in operands
of expressions
TypeScript 2.2 improves checking of nullable operands in expressions.
Specifically, these are now flagged as errors:
notation, but were not allowed to use the . . Starting with TypeScript
2.2 using either should be allowed.
interface StringMap<T> {
[x: string]: T;
}
map["prop1"] = 1;
map.prop2 = 2;
notation.
Support for spread operator
on JSX element children
TypeScript 2.2 adds support for using spread on a JSX element
children. Please see facebook/jsx#57 for more details.
Example
let x: TodoListProps;
will persevere the JSX syntax in the output file, but give it a .js
extension.
keyof and Lookup Types
In JavaScript it is fairly common to have APIs that expect property
names as parameters, but so far it hasn't been possible to express the
type relationships that occur in those APIs.
Example
interface Person {
name: string;
age: number;
location: string;
}
The dual of this is indexed access types, also called lookup types.
Syntactically, they look exactly like an element access, but are written
as types:
Example
You can use this pattern with other parts of the type system to get
type-safe lookups.
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]; // Inferred type is T[K]
}
interface Person {
name: string;
age: number;
location: string;
}
interface PartialPerson {
name?: string;
age?: number;
location?: string;
}
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Same property names, but make the value a promise instead of a concrete one
type Deferred<T> = {
[P in keyof T]: Promise<T[P]>;
};
We're also including two other utility types as well: Record and Pick .
You can also override existing properties and add new ones:
Object rests are the dual of object spreads, in that they can extract
any extra properties that don't get picked up when destructuring an
element:
let obj = { x: 1, y: 1, z: 1 };
let { z, ...obj1 } = obj;
obj1; // {x: number, y:number};
Downlevel Async Functions
This feature was supported before TypeScript 2.1, but only when
targeting ES6/ES2015. TypeScript 2.1 brings the capability to ES3 and
ES5 run-times, meaning you'll be free to take advantage of it no
matter what environment you're using.
Example
tsconfig.json
```ts
function delay(milliseconds: number) {
return new Promise<void>(resolve => {
setTimeout(resolve, milliseconds);
});
}
console.log("World!");
}
dramaticWelcome();
Compiling and running the output should result in the correct behavior
on an ES3/ES5 engine.
Support for external helpers
library ( tslib )
TypeScript injects a handful of helper functions such as __extends for
inheritance, __assign for spread operator in object literals and JSX
elements, and __awaiter for async functions.
The two options left more to be desired; bundling the helpers in every
file was a pain point for customers trying to keep their package size
small. And not including helpers, meant customers had to maintain their
own helpers library.
TypeScript 2.1 allows for including these files in your project once in a
separate module, and the compiler will emit imports to them as
needed.
So given the following input, the resulting .js file will include an
import to tslib and use the __assign helper from it instead of
inlining it.
However, a lot of the time, you might just want to import an existing
module that may not have its own .d.ts file. Previously this was an
error. Starting with TypeScript 2.1 this is now much easier.
Example
Using target --target ES2016 will instruct the compiler not to transform
ES2016-specific features, e.g. ** operator.
Example
let x;
// After that last assignment, TypeScript 2.1 knows that 'x' has type '() =>
number'.
let y = x();
// Thanks to that, it will now tell you that you can't add a number to a function!
console.log(x + y);
// ~~~~~
// Error! Operator '+' cannot be applied to types '() => number' and 'number'.
The same sort of tracking is now also done for empty arrays.
A variable declared with no type annotation and an initial value of []
function f1() {
let x = [];
x.push(5);
x[1] = "hello";
x.unshift(true);
return x; // (string | number | boolean)[]
}
function f2() {
let x = null;
if (cond()) {
x = [];
while (cond()) {
x.push("hello");
}
}
return x; // string[] | null
}
Implicit any errors
One great benefit of this is that you'll see way fewer implicit any
errors when running with --noImplicitAny . Implicit any errors are only
reported when the compiler is unable to know the type of a variable
without a type annotation.
Example
function f3() {
let x = []; // Error: Variable 'x' implicitly has type 'any[]' in some locations
where its type cannot be determined.
x.push(5);
function g() {
x; // Error: Variable 'x' implicitly has an 'any[]' type.
}
}
Better inference for literal
types
String, numeric and boolean literal types (e.g. "abc" , 1 , and true )
were previously inferred only in the presence of an explicit type
annotation. Starting with TypeScript 2.1, literal types are always
inferred for const variables and readonly properties.
Example
const c1 = 1; // Type 1
const c2 = c1; // Type 1
const c3 = "abc"; // Type "abc"
const c4 = true; // Type true
const c5 = cond ? 1 : "abc"; // Type 1 | "abc"
location has an explicit literal type annotation, the const variable gets
a non-widening literal type.
Example
Example
class Base {
x: number;
constructor() {
// return a new object other than `this`
return {
x: 1
};
}
}
Generates:
var Derived = (function(_super) {
__extends(Derived, _super);
function Derived() {
var _this = _super.call(this) || this;
_this.x = 2;
return _this;
}
return Derived;
})(Base);
Example
configs/base.json :
`tsconfig.json`:
```json tsconfig
{
"extends": "./configs/base",
"files": ["main.ts", "supplemental.ts"]
}
tsconfig.nostrictnull.json :
In strict null checking mode, the null and undefined values are not in
the domain of every type and are only assignable to themselves and
any (the one exception being that undefined is also assignable to
void ). So, whereas T and T | undefined are considered synonymous in
regular type checking mode (because undefined is considered a subtype
of any T ), they are different types in strict type checking mode, and
only T | undefined permits undefined values. The same is true for the
relationship of T to T | null .
Example
Example
Example
Example
interface Options {
location?: {
x?: number;
y?: number;
};
}
Type guards for dotted names also work with user defined type guard
functions and the typeof and instanceof operators and do not depend
on the --strictNullChecks compiler option.
The && operator adds null and/or undefined to the type of the
right operand depending on which are present in the type of the left
operand, and the || operator removes both null and undefined from
the type of the left operand in the resulting union type.
because of widening, but in strict null checking mode the inferred type
of z is null (and therefore, absent a type annotation, null is the
only possible value for z ).
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 operation x!
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 to if statements and ?: conditional
expressions and didn't include effects of assignments and control flow
constructs such as return and break 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 (the
narrowed type) at any given location for a local variable or parameter
that is declared to have a union type.
Example
Example
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
itself).
In a function expression or arrow function with no return type
annotation, if the function has no return statements, or only
return statements with expressions of type never , and if the end
point of the function is not reachable (as determined by control
flow analysis), the inferred return type for the function is
never .
In a function with an explicit never return type annotation, all
return statements (if any) must have expressions of type never
Example
interface Point {
readonly x: number;
readonly y: number;
}
var p2 = { x: 1, y: 1 };
var p3: Point = p2; // Ok, read-only alias for p2
p3.x = 5; // Error, p3.x is read-only
p2.x = 5; // Ok, but also changes p3.x because of aliasing
class Foo {
readonly a = 1;
readonly b: string;
constructor() {
this.b = "hello"; // Assignment permitted in constructor
}
}
they expect.
parameters are fake parameters that come first in the parameter list
of a function:
Example
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
class Handler {
info: string;
onClickBad(this: Handler, e: Event) {
// oops, used this here. using this callback would crash at runtime
this.info = e.message;
}
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!
--noImplicitThis
A new flag is also added in TypeScript 2.0 to flag all uses of this
Example
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` if `allowJs` is set to true).
If the `"files"` and `"include"` 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 the `"exclude"` property.
JS files (`.js` and `.jsx`) are also included if `allowJs` is set to true.
If the `"files"` or `"include"` properties are specified, the compiler will instead
include the union of the files included by those two properties.
Files in the directory specified using the `"outDir"` compiler option are always
excluded unless explicitly included via the `"files"` property (even when the
"`exclude`" property is specified).
Files included using `"include"` can be filtered using the `"exclude"` property.
However, files included explicitly using the `"files"` property are always included
regardless of `"exclude"`.
The `"exclude"` property defaults to excluding the `node_modules`,
`bower_components`, and `jspm_packages` directories when not specified.
TypeScript 2.0 provides a set of additional module resolution knops to _inform_ the
compiler where to find declarations for a given module.
## Base URL
##### Example
```json tsconfig
{
"compilerOptions": {
"baseUrl": "./modules"
}
}
Example
Using 'rootDirs', you can inform the compiler of the _roots_ making up this
"virtual" directory;
and thus the compiler can resolve relative modules imports within these "virtual"
directories _as if_ were merged together in one directory.
##### Example
```tree
src
└── views
└── view1.ts (imports './template1')
└── view2.ts
generated
└── templates
└── views
└── template1.ts (imports './view2')
`--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
All imports from a shorthand module will have the any type.
Example
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 as any .
Example
For example:
math-lib.d.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
class Bar {
a: number;
b?: number;
f() {
return 1;
}
g?(): number; // Body of optional method can be omitted
h?() {
return 2;
}
}
Example
class Singleton {
private static instance: Singleton;
private constructor() {}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
Example
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:
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
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
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 under `--
noUnusedLocals`.
##### Example
```ts
import B, { readFile } from "./b";
// ^ Error: `B` declared but never used
readFile();
function returnNull(_a) {
// OK
return null;
}
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 as import d from
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 like rollup.
Trailing commas in function
parameter and argument lists
Trailing comma in function parameter and argument lists are now
allowed. This is an implementation for a Stage-3 ECMAScript proposal
that emits down to valid ES3/ES5/ES6.
Example
function foo(
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 new --skipLibCheck 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 when --skipLibCheck 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.
Example
interface Error {
stack?: string;
}
interface Error {
code?: string;
path?: string;
stack?: string; // OK
}
New --declarationDir
--declarationDir allows for generating declaration files in a different
location than JavaScript files.
Type parameters as
constraints
With TypeScript 1.8 it becomes possible for a type parameter constraint
to reference type parameters from the same type parameter list.
Previously this was an error. This capability is usually referred to as
F-Bounded Polymorphism.
Example
let x = { a: 1, b: 2, c: 3, d: 4 };
assign(x, { b: 10, d: 20 });
assign(x, { e: 0 }); // Error
Control flow analysis errors
TypeScript 1.8 introduces control flow analysis to help catch common
errors that users tend to run into. Read on to get more details, and
check out these errors in action:
Unreachable code
Statements guaranteed to not be executed at run time are now
correctly flagged as unreachable code errors. For instance, statements
following unconditional return , throw , break or continue statements
are considered unreachable. Use --allowUnreachableCode to disable
unreachable code detection and reporting.
Example
function f() {
return; // Automatic Semicolon Insertion triggered at newline
{
x: "string"; // Error: Unreachable code detected.
}
}
Unused labels
Unused labels are also flagged. Just like unreachable code checks,
these are turned on by default; use --allowUnusedLabels to stop
reporting these errors.
Example
Implicit returns
Functions with code paths that do not return a value in JS implicitly
return undefined . These can now be flagged by the compiler as implicit
returns. The check is turned off by default; use --noImplicitReturns to
turn it on.
Example
function f(x) {
// Error: Not all code paths return a value.
if (x) {
return false;
}
Example
switch (x % 2) {
case 0: // Error: Fallthrough case in switch.
console.log("even");
case 1:
console.log("odd");
break;
}
case 2:
console.log("This is *two much*!");
break;
}
Function Components in React
TypeScript now supports Function components. These are lightweight
components that easily compose other components:
// Use parameter destructuring and defaults for easy definition of 'props' type
const Greeter = ({ name = "world" }) => <div>Hello, {name}!</div>;
For this feature and simplified props, be sure to be use the latest
version of react.d.ts.
Simplified props type
management in React
In TypeScript 1.8 with the latest version of react.d.ts (see above),
we've also greatly simplified the declaration of props types.
Specifically:
The ref and key properties will appear with correct types on all
components
The ref property is correctly disallowed on instances of Stateless
Function components
Augmenting global/module
scope from modules
Users can now declare any augmentations that they want to make, or
that any other consumers already have made, to an existing module.
Module augmentations look like plain old ambient module declarations
(i.e. the declare module "foo" { } syntax), and are directly nested either
your own modules, or in another top level ambient external module.
Example
Here map.ts can declare that it will internally patch the Observable type
from observable.ts and add the map method to it.
// observable.ts
export class Observable<T> {
// ...
}
// map.ts
import { Observable } from "./observable";
Observable.prototype.map = /*...*/;
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());
Example
declare global {
interface Array<T> {
mapToNumbers(): number[];
}
}
Array.prototype.mapToNumbers = function () {
/* ... */
};
String literal types
It's not uncommon for an API to expect a specific set of strings for
certain values. For instance, consider a UI library that can move
elements across the screen while controlling the "easing" of the
animation.
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: string; // Can be "ease-in", "ease-out", "ease-in-out"
}
// No errors
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" });
With TypeScript 1.8, we've introduced string literal types. These types
are written the same way string literals are, but in type positions.
Users can now ensure that the type system will catch such errors.
Here's our new AnimationOptions using string literal types:
interface AnimationOptions {
deltaX: number;
deltaY: number;
easing: "ease-in" | "ease-out" | "ease-in-out";
}
Example
will concatenate all modules in the compilation into a single output file
containing multiple module closures.
A module name will be computed for each module based on its relative
location to rootDir .
Example
// file src/a.ts
import * as B from "./lib/b";
export function createA() {
return B.createB();
}
// file src/lib/b.ts
export function createB() {
return {};
}
Results in:
define("lib/b", ["require", "exports"], function (require, exports) {
"use strict";
function createB() {
return {};
}
exports.createB = createB;
});
define("a", ["require", "exports", "lib/b"], function (require, exports, B) {
"use strict";
function createA() {
return B.createB();
}
exports.createA = createA;
});
Support for default import
interop with SystemJS
Module loaders like SystemJS wrap CommonJS modules and expose then
as a default ES6 import. This makes it impossible to share the
definition files between the SystemJS and CommonJS implementation of
the module as the module shape looks different based on the loader.
Example
is compiled to:
And results in
0
1
2
3
4
Improved checking for
for..in statements
Previously the type of a for..in variable is inferred to any ; that
allowed the compiler to ignore invalid uses within the for..in body.
Example
var a: MyObject[];
for (var x in a) {
// Type of x is implicitly string
var obj = a[x]; // Type of obj is MyObject
}
Modules are now emitted with
a "use strict"; prologue
Modules were always parsed in strict mode as per ES6, but for non-ES6
targets this was not respected in the generated code. Starting with
TypeScript 1.8, emitted modules are always in strict mode. This
shouldn't have any visible changes in most code as TS considers most
strict mode errors as errors at compile time, but it means that some
things which used to silently fail at runtime in your TS code, like
assigning to NaN , will now loudly fail. You can reference the MDN
Article on strict mode for a detailed list of the differences between
strict mode and non-strict mode.
Including .js files with --
allowJs
Often there are external source files in your project that may not be
authored in TypeScript. Alternatively, you might be in the middle of
converting a JS code base into TS, but still want to bundle all your
JS code into a single file with the output of your new TS code.
.js files are now allowed as input to tsc . The TypeScript compiler
checks the input .js files for syntax errors, and emits valid output
based on the --target and --module flags. The output can be
combined with other .ts files as well. Source maps are still generated
for .js files just like with .ts files.
Custom JSX factories using -
-reactNamespace
Passing --reactNamespace <JSX factory Name> along with --jsx react allows for
using a different JSX factory from the default React .
The new factory name will be used to call createElement and __spread
functions.
Example
Compiled with:
Results in:
"use strict";
var jsxFactory_1 = require("jsxFactory");
var div = jsxFactory_1.jsxFactory.createElement("div", null, "Hello JSX!");
this -based type guards
TypeScript 1.8 extends user-defined type guard functions to class and
interface methods.
Example
class FileSystemObject {
isFile(): this is File {
return this instanceof File;
}
isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked(): this is Networked & this {
return this.networked;
}
constructor(public path: string, private networked: boolean) {}
}
Microsoft.TypeScript.Compiler
Microsoft.TypeScript.MSBuild
TypeScript-Preview
Prettier error messages from
tsc
We understand that a ton of monochrome output can be a little
difficult on the eyes. Colors can help discern where a message starts
and ends, and these visual clues are important when error output gets
overwhelming.
The old behavior still remains the same if given a directory - the
compiler will try to find a file in the directory named tsconfig.json .
Allow comments in
tsconfig.json
It's always nice to be able to document your configuration!
tsconfig.json now accepts single and multi-line comments.
A couple of limitations
If you add a tsconfig.json file, TypeScript files that are not
considered part of that context are not compiled.
Apache Cordova Apps still have the existing limitation of a single
tsconfig.json file, which must be in either the root or the scripts
folder.
There is no template for tsconfig.json in most project types.
async / await
support in ES6
targets (Node v4+)
TypeScript now supports asynchronous functions for engines that have
native support for ES6 generators, e.g. Node v4 and above.
Asynchronous functions are prefixed with the async keyword; await
Example
In the following example, each input element will be printed out one
at a time with a 400ms delay:
"use strict";
// printDelayed is a 'Promise<void>'
async function printDelayed(elements: string[]) {
for (const element of elements) {
await delay(400);
console.log(element);
}
}
module flag and allows you to specify the module output when targeting
ES6 . This provides more flexibility to target exactly the features you
want in specific runtimes.
Example
It is a common pattern to return the current object (i.e. `this`) from a method to
create [fluent-style APIs](https://wikipedia.org/wiki/Fluent_interface).
For instance, consider the following `BasicCalculator` module:
```ts
export default class BasicCalculator {
public constructor(protected value: number = 0) {}
public square() {
this.value = this.value ** 2;
return this;
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
}
method.
For instance:
interface MyType {
extend<T>(other: T): this & T;
}
ES7 exponentiation operator
TypeScript 1.7 supports upcoming ES7/ES2016 exponentiation operators:
** and **= . The operators will be transformed in the output to
ES3/ES5 using Math.pow .
Example
var x = 2 ** 3;
var y = 10;
y **= 2;
var z = -(4 ** 3);
Example
// Type of f1 is (arg?: { x?: number, y?: number }) => void
function f1({ x = 0, y = 0 } = {}) {}
f2();
f2({}); // Error, x not optional
f2({ x: 1 });
f2({ y: 1 }); // Error, x not optional
f2({ x: 1, y: 1 });
Support for decorators when
targeting ES3
Decorators are now allowed when targeting ES3. TypeScript 1.7 removes
the ES5-specific use of reduceRight from the __decorate helper. The
changes also inline calls Object.getOwnPropertyDescriptor and
Object.defineProperty in a backwards-compatible fashion that allows for a
to clean up the emit for ES5 and later by removing various repetitive
calls to the aforementioned Object methods.
JSX support
JSX is an embeddable XML-like syntax. It is meant to be transformed
into valid JavaScript, but the semantics of that transformation are
implementation-specific. JSX came to popularity with the React library
but has since seen other applications. TypeScript 1.6 supports
embedding, type checking, and optionally compiling JSX directly into
JavaScript.
var x = <any>foo;
// is equivalent to:
var x = foo as any;
Using React
To use JSX-support with React you should use the React typings.
These typings define the JSX namespace so that TypeScript can
correctly check JSX expressions for React. For example:
/// <reference path="react.d.ts" />
interface Props {
name: string;
}
namespace. Please see the [[JSX]] wiki page for defining the JSX
Output generation
TypeScript ships with two JSX modes: preserve and react .
See the [[JSX]] wiki page for more information on using JSX in
TypeScript.
Intersection types
TypeScript 1.6 introduces intersection types, the logical complement of
union types. A union type A | B represents an entity that is either of
type A or type B , whereas an intersection type A & B represents an
entity that is both of type A and type B .
Example
interface Person {
name: string;
}
function f() {
if (true) {
interface T {
x: number;
}
let v: T;
v.x = 5;
} else {
interface T {
x: string;
}
let v: T;
v.x = "hello";
}
}
Local types may reference enclosing type parameters and local class
and interfaces may themselves be generic. For example:
function f3() {
function f<X, Y>(x: X, y: Y) {
class C {
public x = x;
public y = y;
}
return C;
}
let C = f(10, "hello");
let v = new C();
let x = v.x; // number
let y = v.y; // string
}
Class expressions
TypeScript 1.6 adds support for ES6 class expressions. In a class
expression, the class name is optional and, if specified, is only in scope
in the class expression itself. This is similar to the optional name of a
function expression. It is not possible to refer to the class instance
type of a class expression outside the class expression, but the type
can of course be matched structurally. For example:
Some examples:
// Extend built-in types
class ThingA {
getGreeting() {
return "Hello from A";
}
}
class ThingB {
getGreeting() {
return "Hello from B";
}
}
interface Greeter {
getGreeting(): string;
}
interface GreeterConstructor {
new (): Greeter;
}
Examples
var s: Lazy<string>;
s = "eager";
s = () => "lazy";
Examples
function* g() {
for (var i = 0; i < 100; i++) {
yield ""; // infer string
}
yield* otherStringGenerator(); // infer element type of otherStringGenerator
}
Experimental support for
async functions
TypeScript 1.6 introduces experimental support of async functions when
targeting ES6. Async functions are expected to invoke an asynchronous
operation and await its result without blocking normal execution of the
program. This accomplished through the use of an ES6-compatible
Promise implementation, and transposition of the function body into a
compatible form to resume execution when the awaited asynchronous
operation completes.
Example
var p: Promise<number> = /* ... */;
async function fn(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type "number"
return 1 + i;
}
class C {
async m(): Promise<number> {
var i = await p; // suspend execution until 'p' is settled. 'i' has type
"number"
return 1 + i;
}
interface Foo {
y: string;
}
Examples
The exclude list does not support wilcards. It must simply be a list of
files and/or directories.
--init command line option
Run tsc --init in a directory to create an initial tsconfig.json in this
directory with preset defaults. Optionally pass command line arguments
along with --init to be stored in your initial tsconfig.json on
creation.
ES6 Modules
TypeScript 1.5 supports ECMAScript 6 (ES6) modules. ES6 modules are
effectively TypeScript external modules with a new syntax: ES6 modules
are separately loaded source files that possibly import other modules
and provide a number of externally accessible exports. ES6 modules
feature several new export and import declarations. It is recommended
that TypeScript libraries and applications be updated to use the new
syntax, but this is not a requirement. The new ES6 module syntax
coexists with TypeScript's original internal and external module
constructs and the constructs can be mixed and matched at will.
Export Declarations
In addition to the existing TypeScript support for decorating
declarations with export , module members can also be exported using
separate export declarations, optionally specifying different names for
exports using as clauses.
Re-exporting
Using from clause a module can copy the exports of a given module
to the current module without introducing local names.
Default Export
An export default declaration specifies an expression that becomes the
default export of a module:
t
Bare Import
A "bare import" can be used to import a module only for its side-
effects.
import "./polyfills";
For more information about module, please see the ES6 module support
spec.
Destructuring in declarations
and assignments
TypeScript 1.5 adds support to ES6 destructuring declarations and
assignments.
Declarations
A destructuring declaration introduces one or more named variables
and initializes them with values extracted from properties of an object
or elements of an array.
var { x, y, z } = getSomeObject();
function drawText({ text = "", location: [x, y] = [0, 0], bold = false }) {
// Draw text
}
Assignments
Destructuring patterns can also be used in regular assignment
expressions. For instance, swapping two variables can be written as a
single destructuring assignment:
var x = 1;
var y = 2;
[x, y] = [y, x];
namespace keyword
TypeScript used the module keyword to define both "internal modules"
and "external modules"; this has been a bit of confusion for
developers new to TypeScript. "Internal modules" are closer to what
most people would call a namespace; likewise, "external modules" in JS
speak really just are modules now.
Before:
module Math {
export function add(x, y) { ... }
}
After:
namespace Math {
export function add(x, y) { ... }
}
let and const support
ES6 let and const declarations are now supported when targeting ES3
and ES5.
Const
const MAX = 100;
Block scoped
if (true) {
let a = 4;
// use a
} else {
let a = "string";
// use a
}
Example
A decorator is:
an expression
that evaluates to a function
that takes the target, name, and property descriptor as arguments
and optionally returns a property descriptor to install on the
target object
Example
class C {
@readonly
@enumerable(false)
method() { ... }
}
function enumerable(value) {
return function (target, key, descriptor) {
descriptor.enumerable = value;
};
}
Computed properties
Initializing an object with dynamic properties can be a bit of a
burden. Take the following example:
Usage:
and
With ES6’s codepoint escapes, you can cleanly represent that exact
character in strings and template strings with a single escape:
"\u{20bb7}" . TypeScript will emit the string in ES3/ES5 as
"\uD842\uDFB7" .
Tagged template strings in
ES3/ES5
In TypeScript 1.4, we added support for template strings for all
targets, and tagged templates for just ES6. Thanks to some
considerable work done by @ivogabe, we bridged the gap for for
tagged templates in ES3 and ES5.
will be emitted as
Generated JS code:
Example
inlineSources allows for additionally inlining the source .ts file into the
.js file.
Union types
Overview
Union types are a powerful way to express a value that can be one of
several types. For example, you might have an API for running a
program that takes a commandline as either a string , a string[] or a
function that returns a string . You can now write:
interface RunOptions {
program: string;
commandline: string[] | string | (() => string);
}
When reading from a union type, you can see any properties that are
shared by them:
if (opts.length === 0) {
// OK, string and string[] both have 'length' property
console.log("it's empty");
}
Using Type Guards, you can easily work with a variable of a union type:
function formatCommandline(c: string | string[]) {
if (typeof c === "string") {
return c.trim();
} else {
return c.join(" ");
}
}
Stricter Generics
With union types able to represent a wide range of type scenarios,
we've decided to improve the strictness of certain generic calls.
Previously, code like this would (surprisingly) compile without error:
// Previously: No error
// New behavior: Error, no best common type between 'string' and 'number'
var e = equal(42, "hello");
With union types, you can now specify the desired behavior at both the
function declaration site and the call site:
tt f
Better Type Inference
Union types also allow for better type inference in arrays and other
places where you might have multiple kinds of values in a collection:
if (foo) {
console.log(x); // Error, cannot refer to x before its declaration
let x = "hello";
} else {
console.log(x); // Error, x is not declared in this block
}
Type aliases are exactly the same as their original types; they are
simply alternative names.
const enum (completely inlined
enums)
Enums are very useful, but some programs don't actually need the
generated code and would benefit from simply inlining all instances of
enum members with their numeric equivalents. The new const enum
declaration works just like a regular enum for type safety, but erases
completely at compile time.
Compiles to exactly:
var d = 1;
enum MyFlags {
None = 0,
Neat = 1,
Cool = 2,
Awesome = 4,
Best = Neat | Cool | Awesome
}
var b = MyFlags.Best; // emits var b = 7;
-noEmitOnError commandline
option
The default behavior for the TypeScript compiler is to still emit .js
files if there were type errors (for example, an attempt to assign a
string to a number ). This can be undesirable on build servers or
other scenarios where only output from a "clean" build is desired. The
new flag noEmitOnError prevents the compiler from emitting .js code if
there were any errors.
This is now the default for MSBuild projects; this allows MSBuild
incremental build to work as expected, as outputs are only generated
on clean builds.
AMD Module names
By default AMD modules are generated anonymous. This can lead to
problems when other tools are used to process the resulting modules
like a bundlers (e.g. r.js ).
The new amd-module name tag allows passing an optional module name to
the compiler:
//// [amdModule.ts]
///<amd-module name='NamedModule'/>
export class C {}
//// [amdModule.js]
define("NamedModule", ["require", "exports"], function(require, exports) {
var C = (function() {
function C() {}
return C;
})();
exports.C = C;
});
Protected
The new protected modifier in classes works like it does in familiar
languages like C++, C#, and Java. A protected member of a class is
visible only inside subclasses of the class in which it is declared:
class Thing {
protected doSomething() {
/* ... */
}
}
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
Note that in TypeScript 1.4, when accessing an element outside the set
of known indices, a union type is used instead:
x[3] = "world"; // OK
console.log(x[5].toString()); // OK, 'string' and 'number' both have toString
x[6] = true; // Error, boolean isn't number or string
Performance Improvements
The 1.1 compiler is typically around 4x faster than any previous release.
See this blog post for some impressive charts.
Better Module Visibility Rules
TypeScript now only strictly enforces the visibility of types in modules
if the --declaration flag is provided. This is very useful for Angular
scenarios, for example:
module MyControllers {
interface ZooScope extends ng.IScope {
animals: Animal[];
}
export class ZooController {
// Used to be an error (cannot expose ZooScope), but now is only
// an error when trying to generate .d.ts files
constructor(public $scope: ZooScope) {}
/* more code */
}
}
Declaration Files
Introcutoin
Library Structures
By Example
Do's and Don'ts
Deep Dive
Templates
Global Modifying Module
Publishing
Consumption
The Declaration Files section is designed to teach you how to write a
high-quality TypeScript Declaration File. We need to assume basic
familiarity with the TypeScript language in order to get started.
The most common case for learning how .d.ts files work is that you're
typing an npm package with no types. In that case, you can jump
straight to Modules .d.ts.
For example, can you only get it through npm or only from a
CDN?
Modular Libraries
Almost every modern Node.js library falls into the module family.
These type of libraries only work in a JS environment with a module
loader. For example, express only works in Node.js and must be
loaded using the CommonJS require function.
var fs = require("fs");
You'll typically see modular libraries include one of these lines in their
documentation:
or
});
As with global modules, you might see these examples in the
documentation of a UMD module, so be sure to check the code or
documentation.
You should first read module.d.ts for an overview on the way they all
work.
const x = require("foo");
// Note: calling 'x' as a function
const y = x(42);
$(() => {
console.log("hello!");
});
<script src="http://a.great.cdn.for/someLib.js"></script>
function createGreeting(s) {
return "Hello, " + s;
}
or like this:
// Web
window.createGreeting = function (s) {
return "Hello, " + s;
};
// Node
global.createGreeting = function (s) {
return "Hello, " + s;
};
require("fs");
Calls to define(...)
console.log(moment.format());
If you see tests for typeof define , typeof window , or typeof module in
the code of a library, especially at the top of the file, it's almost
always a UMD library.
Documentation for UMD libraries will also often demonstrate a "Using
in Node.js" example showing require , and a "Using in the browser"
example showing using a <script> tag to load the script.
Template
Use the module-plugin.d.ts template.
Consuming Dependencies
There are several kinds of dependencies your library might have. This
section shows how to import them into the declaration file.
Dependencies on Global
Libraries
If your library depends on a global library, use a /// <reference
types directive:
But not
// at top-level
interface CatsKittySettings {}
This guidance also ensures that the library can be transitioned to UMD
without breaking declaration file users.
The Impact of ES6 on Module
Call Signatures
Many popular libraries, such as Express, expose themselves as a callable
function when imported. For example, the typical Express usage looks
like this:
Code
Declaration
Code
Declaration
Code
greet({
greeting: "hello world",
duration: 4000
});
Declaration
interface GreetingSettings {
greeting: string;
duration?: number;
color?: string;
}
Code
function getGreeting() {
return "howdy";
}
class MyGreeter extends Greeter {}
greet("hello");
greet(getGreeting);
greet(new MyGreeter());
Declaration
The greeter object can log to a file or display an alert. You can
provide LogOptions to .log(...) and alert options to .alert(...)
Code
Declaration
Code
Declaration
greeting: string;
showGreeting(): void;
}
Global Variables
Documentation
Code
Declaration
You can call the function greet with a string to show a greeting
to the user.
Code
greet("hello, world");
Declaration
/* WRONG */
function reverse(s: String): String;
/* OK */
function reverse(s: string): string;
In cases where you don't know what type you want to accept, or when
you want to accept anything because you will be blindly passing it
through without interacting with it, you can use unknown .
Callback Types
/* WRONG */
function fn(x: () => any) {
x();
}
Do use the return type void for callbacks whose value will be ignored:
/* OK */
function fn(x: () => void) {
x();
}
/* WRONG */
interface Fetcher {
getObject(done: (data: any, elapsedTime?: number) => void): void;
}
This has a very specific meaning: the done callback might be invoked
with 1 argument or might be invoked with 2 arguments. The author
probably intended to say that the callback might not care about the
elapsedTime parameter, but there's no need to make the parameter
optional to accomplish this -- it's always legal to provide a callback
that accepts fewer arguments.
/* OK */
interface Fetcher {
getObject(done: (data: any, elapsedTime: number) => void): void;
}
Overloads and Callbacks
Don't write separate overloads that differ only on callback arity:
/* WRONG */
declare function beforeAll(action: () => void, timeout?: number): void;
declare function beforeAll(
action: (done: DoneFn) => void,
timeout?: number
): void;
/* OK */
declare function beforeAll(
action: (done: DoneFn) => void,
timeout?: number
): void;
Ordering
Don't put more general overloads before more specific overloads:
/* WRONG */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;
/* OK */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;
/* WRONG */
interface Example {
diff(one: string): number;
diff(one: string, two: string): number;
diff(one: string, two: string, three: boolean): number;
}
/* OK */
interface Example {
diff(one: string, two?: string, three?: boolean): number;
}
Note that this collapsing should only occur when all overloads have the
same return type.
var x: Example;
// When written with overloads, incorrectly an error because of passing 'undefined'
to 'string'
// When written with optionals, correctly OK
x.diff("something", true ? undefined : "hour");
Use Union Types
Don't write overloads that differ by type in only one argument
position:
/* WRONG */
interface Moment {
utcOffset(): number;
utcOffset(b: number): Moment;
utcOffset(b: string): Moment;
}
/* OK */
interface Moment {
utcOffset(): number;
utcOffset(b: number | string): Moment;
}
Note that we didn't make b optional here because the return types
of the signatures differ.
Why: This is important for people who are "passing through" a value to
your function:
Types
If you're reading this guide, you probably already roughly know what a
type in TypeScript is. To be more explicit, though, a type is
introduced with:
Values
As with types, you probably already understand what a value is. Values
are runtime names that we can reference in expressions. For example
let x = 5; creates a value called x .
This may seem confusing, but it's actually very convenient as long as
we don't excessively overload things. Let's look at some useful
aspects of this combining behavior.
Built-in Combinations
Astute readers will notice that, for example, class appeared in both
the type and value lists. The declaration class C { } creates two
things: a type C which refers to the instance shape of the class, and
a value C which refers to the constructor function of the class. Enum
declarations behave similarly.
User Combinations
Let's say we wrote a module file foo.d.ts :
This works well enough, but we might imagine that SomeType and
SomeVar were very closely related such that you'd like them to have
the same name. We can use combining to present these two different
objects (the value and the type) under the same name Bar :
Again, we've used Bar as both a type and a value here. Note that we
didn't have to declare the Bar value as being of the Bar type --
they're independent.
Advanced Combinations
Some kinds of declarations can be combined across multiple
declarations. For example, class C { } and interface C { } can co-exist
and both contribute properties to the C types.
declaration:
interface Foo {
x: number;
}
// ... elsewhere ...
interface Foo {
y: number;
}
let a: Foo = ...;
console.log(a.x + a.y); // OK
class C {}
// ... elsewhere ...
namespace C {
export let x: number;
}
let y = C.x; // OK
namespace X {
export interface Y {}
export class Z {}
}
In this example, the first block creates the following name meanings:
global-plugin.d.ts
global.d.ts
module-class.d.ts
module-function.d.ts
module-plugin.d.ts
module.d.ts
Comparing JavaScript to an
example DTS
function getArrayLength(arr) {
return arr.length;
}
module.exports = {
getArrayLength,
maxInterval,
};
The TypeScript playground can show you the .d.ts equivalent for
JavaScript code. You can try it yourself here.
Default Exports
In CommonJS you can export any value as the default export, for
example here is a regular expression module:
Or a number:
module.exports = 3.142;
module.exports = getArrayLength;
export = getArrayLength;
See Module: Functions for details of how that works, and the Modules
reference page.
Handling Many Consuming
Import
There are many ways to import a module in modern consuming code:
class FastifyInstance {}
function fastify() {
return new FastifyInstance();
}
fastify.FastifyInstance = FastifyInstance;
function getArrayMetadata(arr) {
return {
length: getArrayLength(arr),
firstObject: arr[0],
};
}
module.exports = {
getArrayMetadata,
};
This example is a good case for using generics to provide richer type
information:
Now the type of the array propagates into the ArrayMetadata type.
The types which are exported can then be re-used by consumers of
the modules using either import or import type in TypeScript code or
JSDoc imports.
For example, you may have complex enough types to describe that you
choose to namespace them inside your .d.ts :
// This namespace is merged with the API class and allows for consumers, and this
file
// to have types which are nested away in their own sections.
declare namespace API {
export interface InfoRequest {
id: string;
}
deep dive.
/*~ This is the module template file. You should rename it to index.d.ts
*~ and place it in a folder with the same name as the module.
*~ For example, if you were writing a file for "super-greeter", this
*~ file should be 'super-greeter/index.d.ts'
*/
/*~ If this module is a UMD module that exposes a global variable 'myLib' when
*~ loaded outside a module loader environment, declare that global here.
*~ Otherwise, delete this declaration.
*/
export as namespace myLib;
/*~ You can declare types that are available via importing the module */
export interface SomeType {
name: string;
length: number;
extras?: string[];
}
/*~ You can declare properties of the module using const, let, or var */
export const myField: number;
myLib
+---- index.js
+---- foo.js
+---- bar
+---- index.js
+---- baz.js
var a = require("myLib");
var b = require("myLib/foo");
var c = require("myLib/bar");
var d = require("myLib/bar/baz");
@types/myLib
+---- index.d.ts
+---- foo.d.ts
+---- bar
+---- index.d.ts
+---- baz.d.ts
/*~ This example shows how to have multiple overloads for your function */
export interface GreeterFunction {
(name: string): void
(time: number): void
}
/*~ This is the module plugin template file. You should rename it to index.d.ts
*~ and place it in a folder with the same name as the module.
*~ For example, if you were writing a file for "super-greeter", this
*~ file should be 'super-greeter/index.d.ts'
*/
/*~ On this line, import the module which this module adds to */
import { greeter } from "super-greeter";
/*~ Here, declare the same module as the one you imported above
*~ then we expand the existing declaration of the greeter function
*/
export module "super-greeter" {
export interface GreeterFunction {
/** Greets even better! */
hyperGreet(): void;
}
}
/*~ If this module is a UMD module that exposes a global variable 'myClassLib' when
*~ loaded outside a module loader environment, declare that global here.
*~ Otherwise, delete this declaration.
*/
export as namespace myClassLib;
greet: void;
/*~ If you want to expose types from your module as well, you can
*~ place them in this block.
*~
*~ Note that if you decide to include this namespace, the module can be
*~ incorrectly imported as a namespace object, unless
*~ --esModuleInterop is turned on:
*~ import * as x from '[~THE MODULE~]'; // WRONG! DO NOT DO THIS!
*/
declare namespace MyClass {
export interface MyClassMethodOptions {
width?: number;
height?: number;
}
}
For example, when you want to work with JavaScript code which looks
like:
greeter(2);
greeter("Hello world");
/*~ If this module is a UMD module that exposes a global variable 'myFuncLib' when
*~ loaded outside a module loader environment, declare that global here.
*~ Otherwise, delete this declaration.
*/
export as namespace myFuncLib;
/*~ This example shows how to have multiple overloads for your function */
declare function Greeter(name: string): Greeter.NamedReturnType;
declare function Greeter(length: number): Greeter.LengthReturnType;
/*~ If you want to expose types from your module as well, you can
*~ place them in this block. Often you will want to describe the
*~ shape of the return type of the function; that type should
*~ be declared in here, as this example shows.
*~
*~ Note that if you decide to include this namespace, the module can be
*~ incorrectly imported as a namespace object, unless
*~ --esModuleInterop is turned on:
*~ import * as x from '[~THE MODULE~]'; // WRONG! DO NOT DO THIS!
*/
declare namespace Greeter {
export interface LengthReturnType {
width: number;
height: number;
}
export interface NamedReturnType {
firstName: string;
lastName: string;
}
/*~ If the module also has properties, declare them here. For example,
*~ this declaration says that this code is legal:
*~ import f = require('super-greeter');
*~ console.log(f.defaultName);
*/
export const defaultName: string;
export let defaultLength: number;
}
Global Libraries
A global library is one that can be accessed from the global scope
(i.e. without using any form of import ). Many libraries simply expose
one or more global variables for use. For example, if you were using
jQuery, the $ variable can be used by simply referring to it:
$(() => {
console.log("hello!");
});
<script src="http://a.great.cdn.for/someLib.js"></script>
function createGreeting(s) {
return "Hello, " + s;
}
or like this:
require("fs");
Calls to define(...)
/*~ If you want the name of this library to be a valid type name,
*~ you can do so here.
*~
*~ For example, this allows us to write 'var x: myLib';
*~ Be sure this actually makes sense! If it doesn't, just
*~ delete this declaration and add types inside the namespace below.
*/
interface myLib {
name: string;
length: number;
extras?: string[];
}
//~ There's some class we can create via 'let c = new myLib.Cat(42)'
//~ Or reference e.g. 'function f(c: myLib.Cat) { ... }
class Cat {
constructor(n: number);
If your types are generated by your source code, publish the types
with your source code. Both TypeScript and JavaScript projects can
generate types via --declaration .
{
"name": "awesome",
"author": "Vandelay Industries",
"version": "1.0.0",
"main": "./lib/main.js",
"types": "./lib/main.d.ts"
}
Note that the "typings" field is synonymous with "types" , and could be
used as well.
Also note that if your main declaration file is named index.d.ts and
lives at the root of the package (next to index.js ) you do not need
to mark the "types" property, though it is advisable to do so.
Dependencies
All dependencies are managed by npm. Make sure all the declaration
packages you depend on are marked appropriately in the "dependencies"
{
"name": "browserify-typescript-extension",
"author": "Vandelay Industries",
"version": "1.0.0",
"main": "./lib/main.js",
"types": "./lib/main.d.ts",
"dependencies": {
"browserify": "latest",
"@types/browserify": "latest",
"typescript": "next"
}
}
{
"name": "package-name",
"version": "1.0",
"types": "./index.d.ts",
"typesVersions": {
">=3.1": { "*": ["ts3.1/*"] }
}
}
{
"name": "package-name",
"version": "1.0",
"types": "./index.d.ts",
"typesVersions": {
">=3.2": { "*": ["ts3.2/*"] },
">=3.1": { "*": ["ts3.1/*"] }
}
}
{
name: "package-name",
version: "1.0",
types: "./index.d.ts",
typesVersions: {
// NOTE: this doesn't work!
">=3.1": { "*": ["ts3.1/*"] },
">=3.2": { "*": ["ts3.2/*"] },
},
}
Publish to @types
Packages under the @types organization are published automatically from
DefinitelyTyped using the types-publisher tool. To get your
declarations published as an @types package, please submit a pull
request to DefinitelyTyped. You can find more details in the
contribution guidelines page.
Downloading
Getting type declarations requires no tools apart from npm.
For example, once you’ve npm install -ed your type declarations, you
can use imports and write
or if you’re not using modules, you can just use the global variable
_ .
Note: if the declaration file you are searching for is not present,
you can always contribute one back and help out the next
developer looking for it. Please see the DefinitelyTyped
contribution guidelines page for details.
Javascript
JS Projects Utilizing TypeScript
Type Checking Javascript Files
JSDoc Reference
Createing .d.ts Files from .js files
The type system in TypeScript has different levels of strictness when
working with a codebase:
x = 0; // OK
x = false; // OK?!
You can find the full list of supported JSDoc patterns in JSDoc
Supported Types.
@ts-check
The last line of the previous code sample would raise an error in
TypeScript, but it doesn't by default in a JS project. To enable
errors in your JavaScript files add: // @ts-check to the first line in
your .js files to have TypeScript raise it as an error.
// @ts-check
// @errors: 2322
/** @type {number} */
var x;
x = 0; // OK
x = false; // Not OK
If you have a lot of JavaScript files you want to add errors to then
you can switch to using a jsconfig.json . You can skip checking some
files by adding a // @ts-nocheck comment to files.
TypeScript may offer you errors which you disagree with, in those cases
you can ignore errors on specific lines by adding // @ts-ignore or //
// @ts-check
/** @type {number} */
var x;
x = 0; // OK
// @ts-expect-error
x = false; // Not OK
// @checkJs
// @errors: 2322
class C {
constructor() {
this.constructorOnly = 0;
this.constructorUnknown = undefined;
}
method() {
this.constructorOnly = false;
this.constructorUnknown = "plunkbat"; // ok, constructorUnknown is string |
undefined
this.methodOnly = "ok"; // ok, but methodOnly could also be undefined
}
method2() {
this.methodOnly = true; // also, ok, methodOnly's type is string | boolean |
undefined
}
}
If properties are never set in the class body, they are considered
unknown. If your class has properties that are only read from, add
and then annotate a declaration in the constructor with JSDoc to
specify the type. You don't even have to give a value if it will be
initialised later:
// @checkJs
// @errors: 2322
class C {
constructor() {
/** @type {number | undefined} */
this.prop = undefined;
/** @type {number | undefined} */
this.count;
}
}
// @checkJs
// @errors: 2683 2322
function C() {
this.constructorOnly = 0;
this.constructorUnknown = undefined;
}
C.prototype.method = function () {
this.constructorOnly = false;
this.constructorUnknown = "plunkbat"; // OK, the type is string | undefined
};
CommonJS modules are
supported
In a .js file, TypeScript understands the CommonJS module format.
Assignments to exports and module.exports are recognized as export
declarations. Similarly, require function calls are recognized as module
imports. For example:
class C {}
C.D = class {};
function Outer() {
this.y = 2;
}
Outer.Inner = function () {
this.yy = 2;
};
Outer.innter();
var ns = {};
ns.C = class {};
ns.func = function () {};
ns;
// defaulting to global
var assign =
assign ||
function () {
// code goes here
};
assign.extra = 1;
Object literals are open-
ended
In a .ts file, an object literal that initializes a variable declaration
gives its type to the declaration. No new members can be added that
were not specified in the original literal. This rule is relaxed in a .js
file; object literals have an open-ended type (an index signature) that
allows adding and looking up properties that were not defined
originally. For instance:
var obj = { a: 1 };
obj.b = 2; // Allowed
// @checkJs
// @errors: 2339
/** @type {{a: number}} */
var obj = { a: 1 };
obj.b = 2;
null, undefined, and empty
array initializers are of type
any or any[]
Any variable, parameter or property that is initialized with null or
undefined will have type any, even if strict null checks is turned on.
Any variable, parameter or property that is initialized with [] will have
type any[], even if strict null checks is turned on. The only exception
is for properties that have multiple initializers as described above.
For instance:
// @checkJs
// @strict: false
// @errors: 7006 7006 2554
function bar(a, b) {
console.log(a + " " + b);
}
JSDoc annotated functions are excluded from this rule. Use JSDoc
optional parameter syntax ( [ ] ) to express optionality. e.g.:
/**
* @param {string} [somebody] - Somebody's name.
*/
function sayHello(somebody) {
if (!somebody) {
somebody = "John Doe";
}
console.log("Hello " + somebody);
}
sayHello();
Var-args parameter
declaration inferred from use
of arguments
A function whose body has a reference to the arguments reference is
implicitly considered to have a var-arg parameter (i.e. (...arg: any[])
=> any ). Use JSDoc var-arg syntax to specify the type of the
arguments.
In extends clause
For instance, React.Component is defined to have two type parameters,
Props and State . In a .js file, there is no legal way to specify
these in the extends clause. By default the type arguments will be
any :
/**
* @augments {Component<{a: number}, State>}
*/
class MyComponent extends Component {
render() {
this.props.b; // Error: b does not exist on {a:number}
}
}
f
In JSDoc references
An unspecified type argument in JSDoc defaults to any:
/** @type{Array} */
var x = [];
x.push(1); // OK
x.push("string"); // OK, x is of type Array<any>
/** @type{Array.<number>} */
var y = [];
y.push(1); // OK
y.push("string"); // Error, string is not assignable to number
In function calls
A call to a generic function uses the arguments to infer the type
parameters. Sometimes this process fails to infer any types, mainly
because of lack of inference sources; in these cases, the type
parameters will default to any . For example:
p; // Promise<any>;
Note any tags which are not explicitly listed below (such as @async )
are not yet supported.
@type
@callback
@template
class extensions
Property Modifiers @public , @private , @protected , @readonly
/**
* @type {string}
*/
var s;
/**
* @type {(string | boolean)}
*/
var sb;
/**
* @type {string | boolean}
*/
var sb;
You can specify array types using a variety of syntaxes:
You can also specify object literal types. For example, an object with
properties 'a' (string) and 'b' (number) uses the following syntax:
You can specify map-like and array-like objects using string and
number index signatures, using either standard JSDoc syntax or
TypeScript syntax.
/**
* A map-like object that maps arbitrary `string` properties to `number`s.
*
* @type {Object.<string, number>}
*/
var stringToNumber;
The preceding two types are equivalent to the TypeScript types { [x:
string]: number } and { [x: number]: any } . The compiler understands both
syntaxes.
/**
* @type {*} - can be 'any' type
*/
var star;
/**
* @type {?} - unknown type (same as 'any')
*/
var question;
Casts
TypeScript borrows cast syntax from Closure. This lets you cast types
to other types by adding a @type tag before any parenthesized
expression.
/**
* @type {number | string}
*/
var numberOrString = Math.random() < 0.5 ? "hello" : 100;
var typeAssertedNumber = /** @type {number} */ (numberOrString);
Import types
You can also import declarations from other files using import types.
This syntax is TypeScript-specific and differs from the JSDoc
standard:
// @filename: types.d.ts
export type Pet = {
name: string,
};
// @filename: main.js
/**
* @param p { import("./types").Pet }
*/
function walk(p) {
console.log(`Walking ${p.name}...`);
}
// @filename: types.d.ts
export type Pet = {
name: string,
};
// @filename: main.js
// ---cut---
/**
* @typedef { import("./types").Pet } Pet
*/
/**
* @type {Pet}
*/
var myPet;
myPet.name;
import types can be used to get the type of a value from a module
if you don't know the type, or if it has a large type that is annoying
to type:
// @filename: accounts.d.ts
export const userAccount = {
name: "Name",
address: "An address",
postalCode: "",
country: "",
planet: "",
system: "",
galaxy: "",
universe: "",
};
// @filename: main.js
// ---cut---
/**
* @type {typeof import("./accounts").userAccount }
*/
var x = require("./accounts").userAccount;
@param and @returns
@param uses the same type syntax as @type , but adds a parameter
name. The parameter may also be declared optional by surrounding the
name with square brackets:
/**
* @return {PromiseLike<string>}
*/
function ps() {}
/**
* @returns {{ a: string, b: number }} - May use '@returns' as well as '@return'
*/
function ab() {}
@typedef , @callback , and
@param
@typedef may be used to define complex types. Similar syntax works
with @param .
/**
* @typedef {Object} SpecialType - creates a new type named 'SpecialType'
* @property {string} prop1 - a string property of SpecialType
* @property {number} prop2 - a number property of SpecialType
* @property {number=} prop3 - an optional number property of SpecialType
* @prop {number} [prop4] - an optional number property of SpecialType
* @prop {number} [prop5=42] - an optional number property of SpecialType with
default
*/
/**
* @typedef {object} SpecialType1 - creates a new type named 'SpecialType'
* @property {string} prop1 - a string property of SpecialType
* @property {number} prop2 - a number property of SpecialType
* @property {number=} prop3 - an optional number property of SpecialType
*/
/**
* @callback Predicate
* @param {string} data
* @param {number} [index]
* @returns {boolean}
*/
/**
* @template T
* @param {T} x - A generic parameter that flows through to the return type
* @return {T}
*/
function id(x) {
return x;
}
const a = id("string");
const b = id(123);
const c = id({});
/**
* @template T,U,V
* @template W,X
*/
You can also specify a type constraint before the type parameter
name. Only the first type parameter in a list is constrained:
/**
* @template {string} K - K must be a string or string literal
* @template {{ serious(): string }} Seriousalizable - must have a serious method
* @param {K} key
* @param {Seriousalizable} object
*/
function seriousalize(key, object) {
// ????
}
class C {
/**
* @param {number} data
*/
constructor(data) {
// property types can be inferred
this.name = "foo";
// or set explicitly
/** @type {string | null} */
this.title = null;
// @checkJs
// @errors: 2345 2348
/**
* @constructor
* @param {number} data
*/
function C(data) {
// property types can be inferred
this.name = "foo";
// or set explicitly
/** @type {string | null} */
this.title = null;
this.initialize(data);
}
/**
* @param {string} s
*/
C.prototype.initialize = function (s) {
this.size = s.length;
};
/**
* @this {HTMLElement}
* @param {*} e
*/
function callbackForLater(e) {
this.clientHeight = parseInt(e); // should be fine!
}
@extends
When Javascript classes extend a generic base class, there is nowhere
to specify what the type parameter should be. The @extends tag
provides a place for that type parameter:
/**
* @template T
* @extends {Set<T>}
*/
class SortableSet extends Set {
// ...
}
Note that @extends only works with classes. Currently, there is no way
for a constructor function extend a class.
@enum
The @enum tag allows you to create an object literal whose members
are all of a specified type. Unlike most object literals in Javascript, it
does not allow other members.
JSDocState.SawAsterisk;
Note that @enum is quite different from, and much simpler than,
TypeScript's enum . However, unlike TypeScript's enums, @enum can
have any type:
MathFuncs.add1;
More examples
class Foo {}
// ---cut---
var someObj = {
/**
* @param {string} param1 - Docs on property assignments work
*/
x: function (param1) {},
};
/**
* As do docs on variable assignments
* @return {Window}
*/
let someFunc = function () {};
/**
* And class methods
* @param {string} greeting The greeting to use
*/
Foo.prototype.sayHi = (greeting) => console.log("Hi!");
/**
* And arrow functions expressions
* @param {number} x - A multiplier
*/
let myArrow = (x) => x * x;
/**
* Which means it works for stateless function components in JSX too
* @param {{a: string, b: number}} test - Some param
*/
var sfc = (test) => <div>{test.a.charAt(0)}</div>;
/**
* A parameter can be a class constructor, using Closure syntax.
*
* @param {{new(...args: any[]): object}} C - The class to register
*/
function registerClass(C) {}
/**
* @param {...string} p1 - A 'rest' arg (array) of strings. (treated as 'any')
*/
function fn10(p1) {}
/**
* @param {...string} p1 - A 'rest' arg (array) of strings. (treated as 'any')
*/
function fn9(p1) {
return p1.join();
}
Patterns that are known NOT
to be supported
Referring to objects in the value space as types doesn't work unless
the object also creates a type, like a constructor function.
function aNormalFunction() {}
/**
* @type {aNormalFunction}
*/
var wrong;
/**
* Use 'typeof' instead:
* @type {typeof aNormalFunction}
*/
var right;
/**
* @type {{ a: string, b: number= }}
*/
var wrong;
/**
* Use postfix question on the property name instead:
* @type {{ a: string, b?: number }}
*/
var right;
/**
* @type {?number}
* With strictNullChecks: true -- number | null
* With strictNullChecks: false -- number
*/
var nullable;
You can also use a union type:
/**
* @type {number | null}
* With strictNullChecks: true -- number | null
* With strictNullChecks: false -- number
*/
var unionNullable;
/**
* @type {!number}
* Just has type number
*/
var normal;
Unlike JSDoc's type system, TypeScript only allows you to mark types
as containing null or not. There is no explicit non-nullability -- if
strictNullChecks is on, then number is not nullable. If it is off, then
number is nullable.
Unsupported tags
TypeScript ignores any unsupported JSDoc tags.
// @errors: 2341
// @ts-check
class Car {
constructor() {
/** @private */
this.identifier = 100;
}
printIdentifier() {
console.log(this.identifier);
}
}
@public is always implied and can be left off, but means that a
property can be reached from anywhere.
@private means that a property can only be used within the
containing class.
@protected means that a property can only be used within the
containing class, and all derived subclasses, but not on dissimilar
instances of the containing class.
class Car {
constructor() {
/** @readonly */
this.identifier = 100;
}
printIdentifier() {
console.log(this.identifier);
}
}
This set up means you can own the editor experience of TypeScript-
powered editors without porting your project to TypeScript, or having
to maintain .d.ts files in your codebase. TypeScript supports most
JSDoc tags, you can find the reference here.
Setting up your Project to
emit .d.ts files
To add creation of .d.ts files in your project, you will need to do
up-to four steps:
Adding TypeScript
You can learn how to do this in our installation page.
TSConfig
The TSConfig is a jsonc file which configures both your compiler flags,
and declare where to find files. In this case, you will want a file like
the following:
{
// Change this to match your project
include: ["src/**/*"],
compilerOptions: {
// Tells TypeScript to read JS files, as
// normally they are ignored as source files
allowJs: true,
// Generate d.ts files
declaration: true,
// This compiler run should
// only output d.ts files
emitDeclarationOnly: true,
// Types should go into this directory.
// Removing this would place the .d.ts files
// next to the .js files
outDir: "dist",
},
}
You can learn more about the options in the tsconfig reference. An
alternative to using a TSConfig file is the CLI, this is the same
behavior as a CLI command.
"main":"index.js" index.d.ts
"main":"./dist/index.js" ./dist/index.d.ts
Tips
If you'd like to write tests for your .d.ts files, try tsd.
Project Configurations
tsconfig.json
Compiler Options
Project Reference
Compiler Options with Build Tools
Nightly Builds
Overview
The presence of a tsconfig.json file in a directory indicates that the
directory is the root of a TypeScript project. The tsconfig.json file
specifies the root files and the compiler options required to compile
the project.
When input files are specified on the command line, tsconfig.json files
are ignored.
Examples
Example tsconfig.json files:
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
}, "files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"utilities.ts",
"binder.ts",
"checker.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
"tsc.ts",
"diagnosticInformationMap.generated.ts"
] } ```
For example, if you were writing a project which uses Node.js version
12 and above, then you could use the npm module @tsconfig/node12 :
This lets your tsconfig.json focus on the unique choices for your
project, and not all of the runtime mechanics. There are a few
tsconfig bases already, and we're hoping the community can add more
for different environments.
Recommended
Node 10
Node 12
Deno
React Native
Svelte
Details
The "compilerOptions" property can be omitted, in which case the
compiler's defaults are used. See our full list of supported Compiler
Options.
TSConfig Reference
To learn more about the hundreds of configuration options in the
TSConfig Reference.
Schema
The tsconfig.json Schema can be found at the JSON Schema Store.
Overview
Compiler options can be specified using MSBuild properties within an
MSBuild project.
Example
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<TypeScriptRemoveComments>false</TypeScriptRemoveComments>
<TypeScriptSourceMap>true</TypeScriptSourceMap>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<TypeScriptRemoveComments>true</TypeScriptRemoveComments>
<TypeScriptSourceMap>false</TypeScriptSourceMap>
</PropertyGroup>
<Import
Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\
TypeScript\Microsoft.TypeScript.targets"
Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudi
oVersion)\TypeScript\Microsoft.TypeScript.targets')" />
Mappings
Compiler Option MSBuild Property Name
--allowSyntheticDefaultImports TypeScriptAllowSyntheticDefaultImports
--allowUnreachableCode TypeScriptAllowUnreachableCode
--allowUnusedLabels TypeScriptAllowUnusedLabels
--alwaysStrict TypeScriptAlwaysStrict
--baseUrl TypeScriptBaseUrl
--charset TypeScriptCharset
--declaration TypeScriptGeneratesDeclarations
--declarationDir TypeScriptDeclarationDir
--emitBOM TypeScriptEmitBOM
--emitDecoratorMetadata TypeScriptEmitDecoratorMetadata
--emitDeclarationOnly TypeScriptEmitDeclarationOnly
--esModuleInterop TypeScriptESModuleInterop
--experimentalAsyncFunctions TypeScriptExperimentalAsyncFunctions
--experimentalDecorators TypeScriptExperimentalDecorators
--
forceConsistentCasingInFileNames
TypeScriptForceConsistentCasingInFileNam
--importHelpers TypeScriptImportHelpers
Compiler Option MSBuild Property Name
--inlineSourceMap TypeScriptInlineSourceMap
--inlineSources TypeScriptInlineSources
--isolatedModules TypeScriptIsolatedModules
--jsx TypeScriptJSXEmit
--jsxFactory TypeScriptJSXFactory
--jsxFragmentFactory TypeScriptJSXFragmentFactory
--lib TypeScriptLib
--locale automatic
--mapRoot TypeScriptMapRoot
--module TypeScriptModuleKind
--moduleResolution TypeScriptModuleResolution
Compiler Option MSBuild Property Name
--newLine TypeScriptNewLine
--noEmitHelpers TypeScriptNoEmitHelpers
--noEmitOnError TypeScriptNoEmitOnError
--noFallthroughCasesInSwitch TypeScriptNoFallthroughCasesInSwitch
--noImplicitAny TypeScriptNoImplicitAny
--noImplicitReturns TypeScriptNoImplicitReturns
--noImplicitThis TypeScriptNoImplicitThis
--noImplicitUseStrict TypeScriptNoImplicitUseStrict
--noStrictGenericChecks TypeScriptNoStrictGenericChecks
--noUnusedLocals TypeScriptNoUnusedLocals
--noUnusedParameters TypeScriptNoUnusedParameters
--noLib TypeScriptNoLib
--noResolve TypeScriptNoResolve
--out TypeScriptOutFile
--outDir TypeScriptOutDir
--outFile TypeScriptOutFile
--preserveConstEnums TypeScriptPreserveConstEnums
--preserveSymlinks TypeScriptPreserveSymlinks
Compiler Option MSBuild Property Name
--reactNamespace TypeScriptReactNamespace
--removeComments TypeScriptRemoveComments
--rootDir TypeScriptRootDir
--skipLibCheck TypeScriptSkipLibCheck
--skipDefaultLibCheck TypeScriptSkipDefaultLibCheck
--sourceMap TypeScriptSourceMap
--sourceRoot TypeScriptSourceRoot
--strict TypeScriptStrict
--strictFunctionTypes TypeScriptStrictFunctionTypes
--strictNullChecks TypeScriptStrictNullChecks
--strictPropertyInitialization TypeScriptStrictPropertyInitialization
--stripInternal TypeScriptStripInternal
--suppressExcessPropertyErrors TypeScriptSuppressExcessPropertyErrors
--
suppressImplicitAnyIndexErrors
TypeScriptSuppressImplicitAnyIndexErrors
--target TypeScriptTarget
--useDefineForClassFields TypeScriptUseDefineForClassFields
Users using newer versions of TS, will see a prompt to upgrade their
project on first load.
TypeScriptCompileBlocked
If you are using a different build tool to build your project (e.g.
gulp, grunt , etc.) and VS for the development and debugging
experience, set <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> in
your project. This should give you all the editing support, but not the
build when you hit F5.
Project references are a new feature in TypeScript 3.0 that allow you
to structure your TypeScript programs into smaller pieces.
By doing this, you can greatly improve build times, enforce logical
separation between components, and organize your code in new and
better ways.
We're also introducing a new mode for tsc , the --build flag, that
works hand in hand with project references to enable faster TypeScript
builds.
An Example Project
Let's look at a fairly normal program and see how project references
can help us better organize it. Imagine you have a project with two
modules, converter and units , and a corresponding test file for each:
/src/converter.ts
/src/units.ts
/test/converter-tests.ts
/test/units-tests.ts
/tsconfig.json
The test files import the implementation files and do some testing:
// converter-tests.ts
import * as converter from "../converter";
assert.areEqual(converter.celsiusToFahrenheit(0), 32);
Previously, this structure was rather awkward to work with if you used
a single tsconfig file:
You could use multiple tsconfig files to solve some of those problems,
but new ones would appear:
There's no built-in up-to-date checking, so you end up always
running tsc twice
Invoking tsc twice incurs more startup time overhead
tsc -w can't run on multiple config files at once
- Importing modules from a referenced project will instead load its _output_
declaration file (`.d.ts`)
- If the referenced project produces an `outFile`, the output file `.d.ts` file's
declarations will be visible in this project
- Build mode (see below) will automatically build the referenced project if needed
By separating into multiple projects, you can greatly improve the speed of
typechecking and compiling, reduce memory usage when using an editor, and improve
enforcement of the logical groupings of your program.
## `composite`
## `declarationMap`s
You can also enable prepending the output of a dependency using the `prepend`
option in a reference:
```js
"references": [
{ "path": "../utils", "prepend": true }
]
Prepending a project will include the project's output above the
output of the current project. This works for both .js files and
.d.ts files, and source map files will also be emitted correctly.
tsc will only ever use existing files on disk to do this process, so it's
possible to create a project where a correct output file can't be
generated because some project's output would be present more than
once in the resulting file. For example:
A
^ ^
/ \
B C
^ ^
\ /
D
Because dependent projects make use of .d.ts files that are built
from their dependencies, you'll either have to check in certain build
outputs or build a project after cloning it before you can navigate
the project in an editor without seeing spurious errors. We're working
on a behind-the-scenes .d.ts generation process that should be able
to mitigate this, but for now we recommend informing developers that
they should build after cloning.
You can provide tsc -b with multiple config file paths (e.g. tsc -b src
test ). Just like tsc -p , specifying the config file name itself is
unnecessary if it's named tsconfig.json .
tsc -b Commandline
You can specify any number of config files:
Don't worry about ordering the files you pass on the commandline -
tsc will re-order them if needed so that dependencies are always
built first.
If you check in any build outputs ( .js , .d.ts , .d.ts.map , etc.), you
may need to run a --force build after certain source control
operations depending on whether your source control tool preserves
timestamps between the local copy and the remote copy.
MSBuild
If you have an msbuild project, you can enable build mode by adding
<TypeScriptBuildMode>true</TypeScriptBuildMode>
to your proj file. This will enable automatic incremental build as well as
cleaning.
Overall Structure
With more tsconfig.json files, you'll usually want to use Configuration
file inheritance to centralize your common compiler options. This way
you can change a setting in one file rather than having to edit multiple
files.
in a tsconfig.json file.
Install
npm install @babel/cli @babel/core @babel/preset-typescript --save-dev
.babelrc
{
"presets": ["@babel/preset-typescript"]
}
package.json
{
"scripts": {
"build": "babel --out-file bundle.js main.ts"
},
}
Install
npm install tsify
Using API
var browserify = require("browserify");
var tsify = require("tsify");
browserify()
.add("main.ts")
.plugin("tsify", { noImplicitAny: true })
.bundle()
.pipe(process.stdout);
Install
npm install duo-typescript
Using API
var Duo = require("duo");
var fs = require("fs");
var path = require("path");
var typescript = require("duo-typescript");
Duo(__dirname)
.entry("entry.ts")
.use(typescript())
.run(function (err, results) {
if (err) throw err;
// Write compiled result to output file
fs.writeFileSync(out, results.code);
});
Install
npm install grunt-ts
Basic Gruntfile.js
module.exports = function (grunt) {
grunt.initConfig({
ts: {
default: {
src: ["**/*.ts", "!node_modules/**/*.ts"],
},
},
});
grunt.loadNpmTasks("grunt-ts");
grunt.registerTask("default", ["ts"]);
};
Install
npm install gulp-typescript
Basic gulpfile.js
var gulp = require("gulp");
var ts = require("gulp-typescript");
gulp.task("default", function () {
var tsResult = gulp.src("src/*.ts").pipe(
ts({
noImplicitAny: true,
out: "output.js",
})
);
return tsResult.js.pipe(gulp.dest("built/local"));
});
Install
npm install -g jspm@beta
Install
npm install ts-loader --save-dev
Alternatives:
awesome-typescript-loader
MSBuild
Update project file to include locally installed
Microsoft.TypeScript.Default.props (at the top) and
Microsoft.TypeScript.targets (at the bottom) files:
Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\
TypeScript\Microsoft.TypeScript.Default.props"
Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudi
oVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />
Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\
TypeScript\Microsoft.TypeScript.targets"
Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudi
oVersion)\TypeScript\Microsoft.TypeScript.targets')" />
</Project>
Hit Install
More details can be found at Package Manager Dialog and using nightly
builds with NuGet
Compiler supports configuring how to watch files and directories using
compiler flags in TypeScript 3.8+, and environment variables before
that.
Background
The --watch implementation of the compiler relies on using fs.watch
fs.watchFile uses polling and thus involves CPU cycles. But this is the
most reliable mechanism to get the update on the status of
file/directory. Compiler typically uses fs.watchFile to watch source files,
config files and missing files (missing file references) that means the
CPU usage depends on number of files in the program.
Configuring file watching using
a tsconfig.json
```json tsconfig { // Some typical compiler options "compilerOptions": {
"target": "es2020", "moduleResolution": "node" // ... },
} } ```
If environment variable
TSC_NONPOLLING_WATCHER is set to
true, watches parent directory
of files (just like
default (no value specified)
UseFsEventsOnParentDirectory ).
Otherwise watch files using
fs.watchFile with 250ms as the
timeout for any file
Configuring directory watching
using environment variable
TSC_WATCHDIRECTORY
The watching of directory on platforms that don't support recursive
directory watching natively in node, is supported through recursively
creating directory watcher for the child directories using different
options selected by TSC_WATCHDIRECTORY . Note that on platforms that
support native recursive directory watching (e.g windows) the value of
this environment variable is ignored.
Option Description
Sublime Text
Update the Settings - User file with the following:
For VS 2015:
For VS 2013:
// calling 'foo'
foo();
But assuming we don't know the value of foo - and that's pretty
common - we can't reliably say what results we'll get from trying to
run any of this code. The behavior of each operation depends entirely
on what value we had in the first place. Is foo callable? Does it have
a property called toLowerCase on it? And if it does, is toLowerCase
callable? If all of these values are callable, what do they return? The
answers to these questions are usually things we keep in our heads when
we write JavaScript, and we have to hope we got all the details right.
For some values, such as the primitives string and number , we can
identify their type at runtime using the typeof operator. But for
other things like functions, there's no corresponding runtime mechanism
to identify their types. For example, consider this function:
function fn(x) {
return x.flip();
}
We can observe by reading the code that this function will only work if
given an object with a callable flip property, but JavaScript doesn't
surface this information in a way that we can check while the code is
running. The only way in pure JavaScript to tell what fn does with a
particular value is to call it and see what happens. This kind of
behavior makes it hard to predict what code will do before it runs,
which means it's harder to know what your code is going to do while
you're writing it.
Seen in this way, a type is the concept of describing which values are
legal to pass to fn and which aren't legal. JavaScript only truly
provides dynamic typing - running the code to see what happens.
If we add just a bit of code, save our file, refresh our app, and
immediately see the error, we might be able to isolate the problem
quickly; but that's not always the case. We might not have tested the
feature thoroughly enough, so we might never actually run into a
potential error that would be thrown! Or if we were lucky enough to
witness the error, we might have ended up doing large refactorings
and adding a lot of different code that we're forced to dig through.
Ideally, we could have a tool that helps us find these bugs before our
code runs. That's what a static type-checker like TypeScript does.
Static types systems describe the shapes and behaviors of what our
values will be when we run our programs. A type-checker like
TypeScript uses that information and tells us when things might be
going off the rails.
foo();
Running that last sample with TypeScript will give us an error message before we
run the code in the first place.
## Non-exception Failures
So far we've been discussing certain things like runtime errors - cases where the
JavaScript runtime throws its hands up and tells us that it thinks something is
nonsensical.
Those cases come up because [the ECMAScript specification]
(https://tc39.github.io/ecma262/) has explicit instructions on how the language
should behave when it runs into something unexpected.
For example, the specification says that trying to call something that isn't
callable should throw an error.
Maybe that sounds like "obvious behavior", but you could imagine that accessing a
property that doesn't exist on an object should throw an error too.
Instead, JavaScript gives us different behavior and returns the value `undefined`:
```js
let foo = {
name: "Daniel",
age: 26,
};
Ultimately, a static type system has to make the call over what code
should be flagged as an error in its system, even if it's "valid"
JavaScript that won't immediately throw an error. In TypeScript, the
following code produces an error about location not being defined:
```ts twoslash // @errors: 2339 let foo = { name: "Daniel", age: 26, };
foo.location;
While sometimes that implies a trade-off in what you can express, the intent is to
catch legitimate bugs in our programs.
And TypeScript catches _a lot_ of legitimate bugs.
```ts twoslash
// @noErrors
let someString = "Hello World!";
uncalled functions,
```ts twoslash
// @errors: 2367
const value = Math.random() < 0.5 ? "a" : "b";
if (value !== "a") {
// ...
} else if (value === "b") {
// Oops, unreachable
}
Types for Tooling
TypeScript can catch bugs when we make mistakes in our code. That's
great, but TypeScript can also prevent us from making those mistakes
in the first place.
That means TypeScript can be leveraged for editing code too, and the
core type-checker can provide error messages and code completion as
you type in the editor. That's part of what people often refer to
when they talk about tooling in TypeScript.
app.listen(3000);
TypeScript takes tooling seriously, and that goes beyond completions and errors as
you type.
An editor that supports TypeScript can deliver "quick fixes" to automatically fix
errors, refactorings to easily re-organize code, and useful navigation features for
jumping to definitions of a variable, or finding all references to a given
variable.
All of this is built on top of the type-checker and fully cross-platform, so it's
likely that [your favorite editor has TypeScript support available]
(https://github.com/Microsoft/TypeScript/wiki/TypeScript-Editor-Support).
We've been talking about type-checking, but we haven't yet used our type-_checker_.
Let's get acquainted with our new friend `tsc`, the TypeScript compiler.
First we'll need to grab it via npm.
```sh
npm install -g typescript
This installs the TypeScript Compiler tsc globally. You can use
npx or similar tools if you'd prefer to run tsc from a local
node_modules package instead.
Now let's move to an empty folder and try writing our first
TypeScript program: hello.ts :
Notice there are no frills here; this "hello world" program looks identical to what
you'd write for a "hello world" program in JavaScript.
And now let's type-check it by running the command `tsc` which was installed for us
by the `typescript` package.
```sh
tsc hello.ts
Tada!
Wait, "tada" what exactly? We ran tsc and nothing happened! Well,
there were no type errors, so we didn't get any output in our console
since there was nothing to report.
But check again - we got some file output instead. If we look in our
current directory, we'll see a hello.js file next to hello.ts . That's
the output from our hello.ts file after tsc compiles or transforms it
into a plain JavaScript file. And if we check the contents, we'll see
what TypeScript spits out after it processes a .ts file:
greet("Brendan");
If we run `tsc hello.ts` again, notice that we get an error on the command line!
```txt
Expected 2 arguments, but got 1.
So TypeScript doesn't get in your way. Of course, over time, you may
want to be a bit more defensive against mistakes, and make TypeScript
act a bit more strictly. In that case, you can use the --noEmitOnError
compiler option. Try changing your hello.ts file and running tsc with
that flag:
What we did was add _type annotations_ on `person` and `date` to describe what
types of values `greet` can be called with.
You can read that signature as "`greet` takes a `person` of type `string`, and a
`date` of type `Date`".
With this, TypeScript can tell us about other cases where we might have been called
incorrectly.
For example...
```ts twoslash
// @errors: 2345
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", Date());
```ts twoslash
let foo = "hello there!";
// ^?
Even though we didn't tell TypeScript that foo had the type string
it was able to figure that out. That's a feature, and it's best not to
add annotations when the type system would end up inferring the same
type anyway.
Erased Types
Let's take a look at what happens when we compile with tsc :
``ts twoslash // @showEmit // @target: es5 function greet(person: string, date: Date)
{ console.log( Hello ${person}, today is ${date.toDateString()}!`); }
More on that second point later, but let's now focus on that first point.
Type annotations aren't part of JavaScript (or ECMAScript to be pedantic), so there
really aren't any browsers or other runtimes that can just run TypeScript
unmodified.
That's why TypeScript needs a compiler in the first place - it needs some way to
strip out or transform any TypeScript-specific code so that you can run it.
Most TypeScript-specific code gets erased away, and likewise, here our type
annotations were completely erased.
> **Remember**: Type annotations never change the runtime behavior of your program.
## Downleveling
One other difference from the above was that our template string was rewritten from
```js
`Hello ${person}, today is ${date.toDateString()}!`;
to
noImplicitAny
Recall that in some places, TypeScript doesn't try to infer any types
for us and instead falls back to the most lenient type: any . This
isn't the worst thing that can happen - after all, falling back to any
strictNullChecks
By default, values like null and undefined are assignable to any other
type. This can make writing some code easier, but forgetting to
handle null and undefined is the cause of countless bugs in the world
- some consider it a billion dollar mistake! The strictNullChecks flag
makes handling null and undefined more explicit, and spares us from
worrying about whether we forgot to handle null and undefined .
In this chapter, we'll cover some of the most common types of values
you'll find in JavaScript code, and explain the corresponding ways to
describe those types in TypeScript. This isn't an exhaustive list, and
future chapters will describe more ways to name and use other types.
Types can also appear in many more places than just type annotations.
As we learn about the types themselves, we'll also learn about the
places where we can refer to these types to form new constructs.
We'll start by reviewing the most basic and common types you might
encounter when writing JavaScript or TypeScript code. These will later
form the core building blocks of more complex types.
The primitives: string ,
number , and boolean
JavaScript has three very commonly used primitives: string , number ,
and boolean . Each has a corresponding type in TypeScript. As you
might expect, these are the same names you'd see if you used the
JavaScript typeof operator on a value of those types:
The type names String , Number , and Boolean (starting with capital
letters) are legal, but refer to some special built-in types that
shouldn't appear in your code. Always use string , number , or
boolean .
Arrays
To specify the type of an array like [1, 2, 3] , you can use the syntax
number[] ; this syntax works for any type (e.g. string[] is an array of
strings, and so on). You may also see this written as Array<number> ,
which means the same thing. We'll learn more about the syntax T<U>
The `any` type is useful when you don't want to write out a long type just to
convince TypeScript that a particular line of code is okay.
### `noImplicitAny`
When you don't specify a type, and Typescript can't infer it from context, the
compiler will typically default to any.
You usually want to avoid this, though, because any isn't type-checked.
Use the compiler flag [`noImplicitAny`](/tsconfig#noImplicitAny) to flag any
implicit any as an error.
When you declare a variable using `const`, `var`, or `let`, you can optionally add
a type annotation to explicitly specify the type of the variable:
```ts twoslash
let myName: string = "Alice";
// ^^^^^^^^ Type annotation
For the most part you don't need to explicitly learn the rules of inference.
If you're starting out, try using fewer type annotations than you think - you might
be surprised how few you need for TypeScript to fully understand what's going on.
## Functions
When you declare a function, you can add type annotations after each parameter to
declare what types of parameters the function accepts.
Parameter type annotations go after the parameter name:
```ts twoslash
// Parameter type annotation
function greet(name: string) {
// ^^^^^^^^
console.log("Hello, " + name.toUpperCase() + "!!");
}
```ts twoslash
function getFavoriteNumber(): number {
// ^^^^^^^^
return 26;
}
Much like variable type annotations, you usually don't need a return
type annotation because TypeScript will infer the function's return
type based on its return statements. The type annotation in the above
example doesn't change anything. Some codebases will explicitly specify
a return type for documentation purposes, to prevent accidental
changes, or just for personal preference.
Function Expressions
Function expressions are a little bit different from function
declarations. When a function expression appears in a place where
TypeScript can determine how it's going to be called, the parameters
of that function are automatically given types.
Here's an example:
This process is called _contextual typing_ because the _context_ that the function
occurred in informed what type it should have.
Similar to the inference rules, you don't need to explicitly learn how this
happens, but understanding that it _does_ happen can help you notice when type
annotations aren't needed.
Later, we'll see more examples of how the context that a value occurs in can affect
its type.
## Object Types
Apart from primitives, the most common sort of type you'll encounter is an _object
type_.
This refers to any JavaScript value with properties, which is almost all of them!
To define an object type, we simply list its properties and their types.
```ts twoslash
// The parameter's type annotation is an object type
function printCoord(pt: { x: number; y: number }) {
// ^^^^^^^^^^^^^^^^^^^^^^^^
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });
The type part of each property is also optional. If you don't specify
a type, it will be assumed to be any .
Optional Properties
Object types can also specify that some or all of their properties are
optional. To do this, add a ? after the property name:
In JavaScript, if you access a property that doesn't exist, you'll get the value
`undefined` rather than a runtime error.
Because of this, when you _read_ from an optional property, you'll have to check
for `undefined` before using it.
```ts twoslash
// @errors: 2532
function printName(obj: { first: string; last?: string }) {
// Error - might crash if 'obj.last' wasn't provided!
console.log(obj.last.toUpperCase());
if (obj.last !== undefined) {
// OK
console.log(obj.last.toUpperCase());
}
}
Union Types
TypeScript's type system allows you to build new types out of existing
ones using a large variety of operators. Now that we know how to
write a few types, it's time to start combining them in interesting
ways.
It's easy to _provide_ a value matching a union type - simply provide a type
matching any of the union's members.
If you _have_ a value of a union type, how do you work with it?
TypeScript will only allow you to do things with the union if that thing is valid
for _every_ member of the union.
For example, if you have the union `string | number`, you can't use methods that
are only available on `string`:
```ts twoslash
// @errors: 2339
function printId(id: number | string) {
console.log(id.toUpperCase());
}
The solution is to narrow the union with code, the same as you would
in JavaScript without type annotations. Narrowing occurs when
TypeScript can deduce a more specific type for a value based on the
structure of the code.
For example, TypeScript knows that only a string value will have a
typeof value "string" :
```ts twoslash
function welcomePeople(x: string[] | string) {
if (Array.isArray(x)) {
// Here: 'x' is 'string[]'
console.log("Hello, " + x.join(" and "));
} else {
// Here: 'x' is 'string'
console.log("Welcome lone traveler " + x);
}
}
Sometimes you'll have a union where all the members have something in
common. For example, both arrays and strings have a slice method.
If every member in a union has a property in common, you can use
that property without narrowing:
## Type Aliases
We've been using object types and union types by writing them directly in type
annotations.
This is convenient, but it's common to want to use the same type more than once and
refer to it by a single name.
```ts twoslash
type Point = {
x: number;
y: number;
};
You can actually use a type alias to give a name to any type at all,
not just an object type. For example, a type alias can name a union
type:
```ts twoslash
declare function getInput(): string;
declare function sanitize(str: string): string;
// ---cut---
type UserInputSanitizedString = string;
Type aliases and interfaces are very similar, and in many cases you can choose
between them freely.
Here are the most relevant differences between the two that you should be aware of.
You'll learn more about these concepts in later chapters, so don't worry if you
don't understand all of these right away.
For the most part, you can choose based on personal preference, and TypeScript will
tell you if it needs something to be the other kind of declaration. If you would
like a heuristic, use `interface` until you need to use features from `type`.
## Type Assertions
Sometimes you will have information about the type of a value that TypeScript can't
know about.
In this situation, you can use a _type assertion_ to specify a more specific type:
```ts twoslash
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
You can also use the angle-bracket syntax (except if the code is in a
.tsx file), which is equivalent:
TypeScript only allows type assertions which convert to a _more specific_ or _less
specific_ version of a type.
This rule prevents "impossible" coercions like:
```ts twoslash
// @errors: 2352
const x = "hello" as number;
Sometimes this rule can be too conservative and will disallow more
complex coercions that might be valid. If this happens, you can use
two assertions, first to any (or unknown , which we'll introduce later),
then to the desired type:
## Literal Types
In addition to the general types `string` and `number`, we can refer to _specific_
strings and numbers in type positions.
```ts twoslash
// @errors: 2322
let x: "hello" = "hello";
// OK
x = "hello";
// ...
x = "howdy";
It's not much use to have a variable that can only have one value!
But by combining literals into unions, you can express a much more
useful thing - for example, functions that only accept a certain set
of known values:
```ts twoslash // @errors: 2345 function printText(s: string, alignment:
"left" | "right" | "center") { // … } printText("Hello, world",
"left"); printText("G'day, mate", "centre");
```ts twoslash
function compare(a: string, b: string): -1 | 0 | 1 {
return a === b ? 0 : a > b ? 1 : -1;
}
When you initialize a variable with an object, TypeScript assumes that the
properties of that object might change values later.
For example, if you wrote code like this:
```ts twoslash
declare const someCondition: boolean;
// ---cut---
const obj = { counter: 0 };
if (someCondition) {
obj.counter = 1;
}
```ts twoslash
declare function handleRequest(url: string, method: "GET" | "POST"): void;
// ---cut---
const req = { url: "https://example.com", method: "GET" as "GET" };
/* or */
handleRequest(req.url, req.method as "GET");
The first change means "I intend for req.method to always have the
literal type "GET" ", preventing the possible assignment of "GUESS" to
that field. The second change means "I know for other reasons that
req.method has the value "GET" ".
null and undefined
JavaScript has two primitive values used to signal absent or uninitialized
value: null and undefined .
TypeScript has two corresponding types by the same names. How these
types behave depends on whether you have the strictNullChecks option
on.
strictNullChecks off
With strictNullChecks off, values that might be null or undefined can
still be accessed normally, and the values null and undefined can be
assigned to a property of any type. This is similar to how languages
without null checks (e.g. C#, Java) behave. The lack of checking for
these values tends to be a major source of bugs; we always recommend
people turn strictNullChecks on if it's practical to do so in their
codebase.
strictNullChecks on
With strictNullChecks on, when a value is null or undefined , you will
need to test for those values before using methods or properties on
that value. Just like checking for undefined before using an optional
property, we can use narrowing to check for values that might be
null :
TypeScript also has a special syntax for removing `null` and `undefined` from a
type without doing any explicit checking.
Writing `!` after any expression is effectively a type assertion that the value
isn't `null` or `undefined`:
```ts twoslash
function liveDangerously(x?: number | null) {
// No error
console.log(x!.toFixed());
}
Just like other type assertions, this doesn't change the runtime
behavior of your code, so it's important to only use ! when you
know that the value can't be null or undefined .
bigint
// Creating a BigInt via the literal syntax let bar: bigint = 100n;
You can learn more about BigInt in [the TypeScript 3.2 release notes]
(/docs/handbook/release-notes/typescript-3-2.html#bigint).
##### `symbol`
```ts twoslash
// @errors: 2367
const firstName = Symbol("name");
const secondName = Symbol("name");
```ts twoslash
// @errors: 2365
function padLeft(padding: number | string, input: string) {
return new Array(padding + 1).join(" ") + input;
}
While it might not look like much, there's actually a lot going under the covers
here.
Much like how TypeScript analyzes runtime values using static types, it overlays
type analysis on JavaScript's runtime control flow constructs like `if/else`,
conditional ternaries, loops, truthiness checks, etc., which can all affect those
types.
Within our `if` check, TypeScript sees `typeof padding === "number"` and
understands that as a special form of code called a _type guard_.
TypeScript follows possible paths of execution that our programs can take to
analyze the most specific possible type of a value at a given position.
It looks at these special checks (called _type guards_) and assignments, and the
process of refining types to more specific types than declared is called
_narrowing_.
In many editors we can observe these types as they change, and we'll even do so in
our examples.
```ts twoslash
function padLeft(padding: number | string, input: string) {
if (typeof padding === "number") {
return new Array(padding + 1).join(" ") + input;
// ^?
}
return padding + input;
// ^?
}
"string"
"number"
"bigint"
"boolean"
"symbol"
"undefined"
"object"
"function"
Users with enough experience might not be surprised, but not everyone has run into
this in JavaScript; luckily, TypeScript lets us know that `strs` was only narrowed
down to `string[] | null` instead of just `string[]`.
This might be a good segue into what we'll call "truthiness" checking.
# Truthiness narrowing
Truthiness might not be a word you'll find in the dictionary, but it's very much
something you'll hear about in JavaScript.
```ts twoslash
function getUsersOnlineMessage(numUsersOnline: number) {
if (numUsersOnline) {
return `There are ${numUsersOnline} online now!`;
}
return "Nobody's here. :(";
}
NaN
undefined
all coerce to false , and other values get coerced true . You can
always coerce values to boolean s by running them through the Boolean
It's fairly popular to leverage this behavior, especially for guarding against
values like `null` or `undefined`.
As an example, let's try using it for our `printAll` function.
```ts twoslash
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
}
}
You'll notice that we've gotten rid of the error above by checking if
strs is truthy. This at least prevents us from dreaded errors when
we run our code like:
TypeScript doesn't hurt us here at all, but this is behavior worth noting if you're
less familiar with JavaScript.
TypeScript can often help you catch bugs early on, but if you choose to do
_nothing_ with a value, there's only so much that it can do without being overly
prescriptive.
If you want, you can make sure you handle situations like these with a linter.
One last word on narrowing by truthiness is that Boolean negations with `!` filter
out from negated branches.
```ts twoslash
function multiplyAll(
values: number[] | undefined,
factor: number
): number[] | undefined {
if (!values) {
return values;
} else {
return values.map((x) => x * factor);
}
}
Equality narrowing
TypeScript also uses switch statements and equality checks like === ,
!== , == , and != to narrow types. For example:
When we checked that `x` and `y` are both equal in the above example, TypeScript
knew their types also had to be equal.
Since `string` is the only common type that both `x` and `y` could take on,
TypeScript knows that `x` and `y` must be a `string` in the first branch.
Checking against specific literal values (as opposed to variables) works also.
In our section about truthiness narrowing, we wrote a `printAll` function which was
error-prone because it accidentally didn't handle empty strings properly.
Instead we could have done a specific check to block out `null`s, and TypeScript
still correctly removes `null` from the type of `strs`.
```ts twoslash
function printAll(strs: string | string[] | null) {
if (strs !== null) {
if (typeof strs === "object") {
for (const s of strs) {
// ^?
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
// ^?
}
}
}
} }
## `instanceof` narrowing
```ts twoslash
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
// ^?
} else {
console.log(x.toUpperCase());
// ^?
}
}
Assignments
As we mentioned earlier, when we assign to any variable, TypeScript
looks at the right side of the assignment and narrows the left side
appropriately.
console.log(x); // ^? x = "goodbye!";
console.log(x); // ^?
If we'd assigned a `boolean` to `x`, we'd have seen an error since that wasn't part
of the declared type.
```ts twoslash
// @errors: 2322
let x = Math.random() < 0.5 ? 10 : "hello world!";
// ^?
x = 1;
console.log(x);
// ^?
x = true;
console.log(x);
// ^?
Control flow analysis
Up until this point, we've gone through some basic examples of how
TypeScript narrows within specific branches. But there's a bit more
going on than just walking up from every variable and looking for type
guards in if s, while s, conditionals, etc. For example
This analysis of code based on reachability is called _control flow analysis_, and
TypeScript uses this flow analysis to narrow types as it encounters type guards and
assignments.
When a variable is analyzed, control flow can split off and re-merge over and over
again, and that variable can be observed to have a different type at each point.
```ts twoslash
function foo() {
let x: string | number | boolean;
console.log(x);
// ^?
return x;
// ^?
}
Discriminated unions
Most of the examples we've looked at so far have focused around
narrowing single variables with simple types like string , boolean , and
number . While this is common, most of the time in JavaScript we'll be
dealing with slightly more complex structures.
For some motivation, let's imagine we're trying to encode shapes like
circles and squares. Circles keep track of their radii and squares keep
track of their side lengths. We'll use a field called kind to tell which
shape we're dealing with. Here's a first attempt at defining Shape .
Notice we're using a union of string literal types: `"circle"` and `"square"` to
tell us whether we should treat the shape as a circle or square respectively.
By using `"circle" | "square"` instead of `string`, we can avoid misspelling
issues.
```ts twoslash
// @errors: 2367
interface Shape {
kind: "circle" | "square";
radius?: number;
sideLength?: number;
}
// ---cut---
function handleShape(shape: Shape) {
// oops!
if (shape.kind === "rect") {
// ...
}
}
We can write a getArea function that applies the right logic based on
if it's dealing with a circle or square. We'll first try dealing with
circles.
```ts twoslash
// @errors: 2532
interface Shape {
kind: "circle" | "square";
radius?: number;
sideLength?: number;
}
// ---cut---
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
}
}
Hmm, TypeScript still doesn't know what to do here. We've hit a point
where we know more about our values than the type checker does. We
could try to use a non-null assertion (a ! after shape.radius ) to say
that radius is definitely present.
The problem with this encoding of `Shape` is that the type-checker doesn't have any
way to know whether or not `radius` or `sideLength` are present based on the `kind`
property.
We need to communicate what _we_ know to the type checker.
With that in mind, let's take another swing at defining `Shape`.
```ts twoslash
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
Here, we've properly separated Shape out into two types with
different values for the kind property, but radius and sideLength are
declared as required properties in their respective types.
Let's see what happens here when we try to access the radius of a
Shape .
```ts twoslash
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
// ---cut---
function getArea(shape: Shape) {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
// ^?
}
}
That got rid of the error! When every type in a union contains a
common property with literal types, TypeScript considers that to be a
discriminated union, and can narrow out the members of the union.
The same checking works with switch statements as well. Now we can
try to write our complete getArea without any pesky ! non-null
assertions.
> As an aside, try playing around with the above example and remove some of the
return keywords.
> You'll see that type-checking can help avoid bugs when accidentally falling
through different clauses in a `switch` statement.
Discriminated unions are useful for more than just talking about circles and
squares.
They're good for representing any sort of messaging scheme in JavaScript, like when
sending messages over the network (client/server communication), or encoding
mutations in a state management framework.
```ts twoslash
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
// ---cut---
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}
Exhaustiveness checking
Functions are the basic building block of any application, whether
they're local functions, imported from another module, or methods on
a class. They're also values, and just like other values, TypeScript has
many ways to describe how functions can be called. Let's learn about
how to write types that describe functions.
Function Type Expressions
The simplest way to describe a function is with a function type
expression. These types are syntactically similar to arrow functions:
greeter(printToConsole);
The syntax `(a: string) => void` means "a function with one parameter, named `a`,
of type string, that doesn't have a return value".
Just like with function declarations, if a parameter type isn't specified, it's
implicitly `any`.
> Note that the parameter name is **required**. The function type `(string) =>
void` means "a function with a parameter named `string` of type `any`"!
```ts twoslash
type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
// ...
}
Call Signatures
In JavaScript, functions can have properties in addition to being
callable. However, the function type expression syntax doesn't allow
for declaring properties. If we want to describe something callable
with properties, we can write a call signature in an object type:
Note that the syntax is slightly different compared to a function type expression -
use `:` between the parameter list and the return type rather than `=>`.
## Construct Signatures
```ts twoslash
type SomeObject = any;
// ---cut---
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
## Generic Functions
It's common to write a function where the types of the input relate to the type of
the output, or where the types of two inputs are related in some way.
Let's consider for a moment a function that returns the first element of an array:
```ts twoslash
function firstElement(arr: any[]) {
return arr[0];
}
This function does its job, but unfortunately has the return type any .
It'd be better if the function returned the type of the array
element.
By adding a type parameter `T` to this function and using it in two places, we've
created a link between the input of the function (the array) and the output (the
return value).
Now when we call it, a more specific type comes out:
```ts twoslash
declare function firstElement<T>(arr: T[]): T;
// ---cut---
// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);
Inference
Note that we didn't have to specify T in this sample. The type was
inferred - chosen automatically - by TypeScript.
```ts twoslash function map(arr: E[], func: (arg: E) => O): O[] { return
arr.map(func); }
Note that in this example, TypeScript could infer both the type of the `E` type
parameter (from the given `string` array), as well as the type `O` based on the
return value of the function expression.
### Constraints
We've written some generic functions that can work on _any_ kind of value.
Sometimes we want to relate two values, but can only operate on a certain subset of
values.
In this case, we can use a _constraint_ to limit the kinds of types that a type
parameter can accept.
```ts twoslash
// @errors: 2345 2322
function longest<T extends { length: number }>(a: T, b: T) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
```ts twoslash
declare function minimumLength<T extends { length: number }>(
obj: T,
minimum: number
): T;
// ---cut---
// 'arr' gets value { length: 6 }
const arr = minimumLength([1, 2, 3], 6);
// and crashes here because arrays have
// a 'slice' method, but not the returned object!
console.log(arr.slice(0));
```ts twoslash
// @errors: 2322
declare function combine<T>(arr1: T[], arr2: T[]): T[];
// ---cut---
const arr = combine([1, 2, 3], ["hello"]);
Writing generic functions is fun, and it can be easy to get carried away with type
parameters.
Having too many type parameters or using constraints where they aren't needed can
make inference less successful, frustrating callers of your function.
```ts twoslash
function firstElement1<T>(arr: T[]) {
return arr[0];
}
// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);
Rule: When possible, use the type parameter itself rather than
constraining it
```ts twoslash function filter1(arr: T[], func: (arg: T) => boolean): T[]
{ return arr.filter(func); }
function filter2 boolean>(arr: T[], func: F): T[] { return
arr.filter(func); }
We've created a type parameter `F` that _doesn't relate two values_.
That's always a red flag, because it means callers wanting to specify type
arguments have to manually specify an extra type argument for no reason.
`F` doesn't do anything but make the function harder to read and reason about!
```ts twoslash
function greet<S extends string>(s: S) {
console.log("Hello, " + s);
}
greet("world");
Remember, type parameters are for _relating the types of multiple values_.
If a type parameter is only used once in the function signature, it's not relating
anything.
> **Rule**: If a type parameter only appears in one location, strongly reconsider
if you actually need it
## Optional Parameters
```ts twoslash
function f(n: number) {
console.log(n.toFixed()); // 0 arguments
console.log(n.toFixed(3)); // 1 argument
}
We can model this in TypeScript by marking the parameter as optional
with ? :
Although the parameter is specified as type `number`, the `x` parameter will
actually have the type `number | undefined` because unspecified parameters in
JavaScript get the value `undefined`.
```ts twoslash
function f(x = 10) {
// ...
}
Once you've learned about optional parameters and function type expressions, it's
very easy to make the following mistakes when writing functions that invoke
callbacks:
```ts twoslash
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i);
}
}
What this _actually_ means is that _`callback` might get invoked with one
argument_.
In other words, the function definition says that the implementation might look
like this:
```ts twoslash
// @errors: 2532
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
for (let i = 0; i < arr.length; i++) {
// I don't feel like providing the index today
callback(arr[i]);
}
}
In turn, TypeScript will enforce this meaning and issue errors that
aren't really possible:
> When writing a function type for a callback, _never_ write an optional parameter
unless you intend to _call_ the function without passing that argument
## Function Overloads
Some JavaScript functions can be called in a variety of argument counts and types.
For example, you might write a function to produce a `Date` that takes either a
timestamp (one argument) or a month/day/year specification (three arguments).
```ts twoslash
// @errors: 2575
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3);
Again, the signature used to write the function body can't be "seen" from the
outside.
> The signature of the _implementation_ is not visible from the outside.
> When writing an overloaded function, you should always have _two_ or more
signatures above the implementation of the function.
```ts twoslash
// @errors: 2394
function fn(x: boolean): void;
// Argument type isn't right
function fn(x: string): void;
function fn(x: boolean) {}
Like generics, there are a few guidelines you should follow when using function
overloads.
Following these principles will make your function easier to call, easier to
understand, and easier to implement.
```ts twoslash
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
return x.length;
}
Because both overloads have the same argument count and same return type, we can
instead write a non-overloaded version of the function:
```ts twoslash
function len(x: any[] | string) {
return x.length;
}
This is much better! Callers can invoke this with either sort of value,
and as an added bonus, we don't have to figure out a correct
implementation signature.
void
void represents the return value of functions which don't return a
value. It's the inferred type any time a function doesn't have any
return statements, or doesn't return any explicit value from those
return statements:
### `object`
The special type `object` refers to any value that isn't a primitive (`string`,
`number`, `boolean`, `symbol`, `null`, or `undefined`).
This is different from the _empty object type_ `{ }`, and also different from the
global type `Object`.
You can read the reference page about [[The global types]] for information on what
`Object` is for - long story short, don't ever use `Object`.
Note that in JavaScript, function values are objects: They have properties, have
`Object.prototype` in their prototype chain, are `instanceof Object`, you can call
`Object.keys` on them, and so on.
For this reason, function types are considered to be `object`s in TypeScript.
### `unknown`
```ts twoslash
// @errors: 2571
function f1(a: any) {
a.b(); // OK
}
function f2(a: unknown) {
a.b();
}
This is useful when describing function types because you can describe
functions that accept any value without having any values in your
function body.
Conversely, you can describe a function that returns a value of
unknown type:
### `never`
```ts twoslash
function fail(msg: string): never {
throw new Error(msg);
}
### `Function`
The global type `Function` describes properties like `bind`, `call`, `apply`, and
others present on all function values in JavaScript.
It also has the special property that values of type `Function` can always be
called; these calls return `any`:
```ts twoslash
function doSomething(f: Function) {
f(1, 2, 3);
}
This is an untyped function call and is generally best avoided because
of the unsafe any return type.
Rest Parameters
In addition to using optional parameters or overloads to make
functions that can accept a variety of fixed argument counts, we can
also define functions that take an unbounded number of arguments
using rest parameters.
A rest parameter appears after all other parameters, and uses the
... syntax:
Conversely, we can _provide_ a variable number of arguments from an array using the
spread syntax.
For example, the `push` method of arrays takes any number of arguments:
```ts twoslash
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);
Note that in general, TypeScript does not assume that arrays are
immutable. This can lead to some surprising behavior:
The best fix for this situation depends a bit on your code, but in general a
`const` context is the most straightforward solution:
```ts twoslash
// Inferred as 2-length tuple
const args = [8, 5] as const;
// OK
const angle = Math.atan2(...args);
Parameter Destructuring
Background reading: Destructuring Assignment
function sum({ a, b, c }) {
console.log(a + b + c);
}
sum({ a: 10, b: 3, c: 9 });
The type annotation for the object goes after the destructuring
syntax:
This can look a bit verbose, but you can use a named type here as well:
```ts twoslash
// Same as prior example
type ABC = { a: number; b: number; c: number };
function sum({ a, b, c }: ABC) {
console.log(a + b + c);
}
Assignability of Functions
Thus, the following implementations of the type () => void are valid:
```ts twoslash type voidFunc = () => void
```ts twoslash
type voidFunc = () => void
const v2 = f2()
const v3 = f3()
This behavior exists so that the following code is valid even though
Array.prototype.push returns a number and the Array.prototype.forEach
There is one other special case to be aware of, when a literal function definition
has a `void` return type, that function must **not** return anything.
```ts twoslash
function f2 (): void {
// @ts-expect-error
return true
}
v1 handbook
v2 handbook
FAQ - "Why are functions returning non-void assignable to
function returning void?"
In JavaScript, the fundamental way that we group and pass around
relevant data is through objects. In TypeScript, we represent those
through object types.
```ts twoslash
interface Person {
// ^^^^^^
name: string;
age: number;
}
or a type alias.
## Property Modifiers
Each property in an object type can specify a couple of things: the type, whether
the property is optional, and whether the property can be written to.
Much of the time, we'll find ourselves dealing with objects that _might_ have a
property set.
In those cases, we can mark those properties as _optional_ by adding a question
mark (`?`) to the end of their names.
```ts twoslash
interface Shape {}
declare function getShape(): Shape;
// ---cut---
interface PaintOptions {
shape: Shape;
xPos?: number;
// ^
yPos?: number;
// ^
}
In this example, both xPos and yPos are considered optional. We can
choose to provide either of them, so every call above to paintShape is
valid. All optionality really says is that if the property is set, it better
have a specific type.
```ts twoslash interface Shape {} declare function getShape(): Shape;
We can also read from those properties - but when we do under `strictNullChecks`,
TypeScript will tell us they're potentially `undefined`.
```ts twoslash
interface Shape {}
declare function getShape(): Shape;
interface PaintOptions {
shape: Shape;
xPos?: number;
yPos?: number;
}
// ---cut---
function paintShape(opts: PaintOptions) {
let xPos = opts.xPos;
// ^?
let yPos = opts.yPos;
// ^?
// ...
}
In JavaScript, even if the property has never been set, we can still
access it - it's just going to give us the value undefined . We can
just handle undefined specially.
```ts twoslash
interface Shape {}
declare function getShape(): Shape;
interface PaintOptions {
shape: Shape;
xPos?: number;
yPos?: number;
}
// ---cut---
function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) {
console.log("x coordinate at", xPos);
// ^?
console.log("y coordinate at", yPos);
// ^?
// ...
}
```ts twoslash
// @errors: 2540
interface SomeType {
readonly prop: string;
}
${home.resident.name}! ); home.resident.age++; }
```ts twoslash
interface Person {
name: string;
age: number;
}
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
// works
let readonlyPerson: ReadonlyPerson = writablePerson;
In some situations that's enough, but addresses often have a unit number associated
with them if the building at an address has multiple units.
We can then describe an `AddressWithUnit`.
This does the job, but the downside here is that we had to repeat all
the other fields from BasicAddress when our changes were purely
additive. Instead, we can extend the original BasicAddress type and
just add the new fields that are unique to AddressWithUnit .
```ts twoslash
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
Here, we've intersected `Colorful` and `Circle` to produce a new type that has all
the members of `Colorful` _and_ `Circle`.
```ts twoslash
// @errors: 2345
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
// ---cut---
function draw(circle: Colorful & Circle) {
console.log(`Color was ${circle.color}`);
console.log(`Radius was ${circle.radius}`);
}
// okay
draw({ color: "blue", radius: 42 });
// oops
draw({ color: "red", raidus: 42 });
Interfaces vs. Intersections
We just looked at two ways to combine types which are similar, but are
actually subtly different. With interfaces, we could use an extends
For example, two types can declare the same property in an interface.
TODO
Generic Object Types
Let's imagine a Box type that can contain any value - string s,
number s, Giraffe s, whatever.
Right now, the `contents` property is typed as `any` which works, but can lead to
accidents down the line.
We could instead use `unknown`, but that would mean that in cases where we already
know the type of `contents`, we'd need to do precautionary checks, or use error-
prone type assertions.
```ts twoslash
interface Box {
contents: unknown;
}
let x: Box = {
contents: "hello world",
};
```ts twoslash
interface NumberBox {
contents: number;
}
interface StringBox {
contents: string;
}
interface BooleanBox {
contents: boolean;
}
// ---cut---
function setContents(box: StringBox, newContents: string): void;
function setContents(box: NumberBox, newContents: number): void;
function setContents(box: BooleanBox, newContents: boolean): void;
function setContents(box: { contents: any }, newContents: any) {
box.contents = newContents;
}
You might read this as "A `Box` of `T` is something whose `contents` have type `T`.
Later on, when we refer to `Box`, we have to give some _type arguments_ in place of
`T`.
```ts twoslash
interface Box<T> {
contents: T;
}
// ---cut---
let box: Box<string>;
Think of Box as a template for a real type, where T is a placeholder
that will get replaced with some other type. When TypeScript sees
Box<string> , it will replace every instance of T in Box<T> with string ,
and end up working with something like { contents: string } . In other
words, Box<string> and our earlier StringBox work identically.
`Box` is reusable in that `T` can be substituted with anything, and that means that
when we need a box for a new type, we don't need to declare a new box type at all
(though we certainly could if we wanted to).
```ts twoslash
interface Box<T> {
contents: T;
}
interface Apple {
// ....
}
This also means that we can avoid overloads entirely by instead using
generic functions.
```ts twoslash
interface Box<T> {
contents: T;
}
In fact, given that type aliases can describe more than just object types, we can
occasionally write some generic helper types as well.
```ts twoslash
// @errors: 2575
type OrNull<T> = T | null;
and Array<string> .
Much like the `Box` type above, `Array` itself is a generic type.
```ts twoslash
// @noLib: true
interface Number {}
interface String {}
interface Boolean {}
interface Symbol {}
// ---cut---
interface Array<T> {
/**
* Gets or sets the length of the array.
*/
length: number;
/**
* Removes the last element from an array and returns it.
*/
pop(): T | undefined;
/**
* Appends new elements to an array, and returns the new length of the array.
*/
push(...items: T[]): number;
// ...
}
Much like the `readonly` modifier for properties, it's mainly a tool we can use for
intent.
When we see a function that returns `ReadonlyArray`s, it tells us we're not meant
to change the contents at all, and when we see a function that consumes
`ReadonlyArray`s, it tells us that we can pass any array into that function without
worrying that it will change its contents.
```ts twoslash
// @errors: 2693
new ReadonlyArray("red", "green", "blue");
```ts twoslash
// @errors: 2339
function doStuff(values: readonly string[]) {
// ^^^^^^^^^^^^^^^^^
// We can read from 'values'...
const copy = values.slice();
console.log(`The first value is ${values[0]}`);
One last thing to note is that unlike the readonly property modifier,
assignability isn't bidirectional between regular Array s and
ReadonlyArray s.
x = y; y = x;
A _tuple type_ is another sort of `Array` type that knows exactly how many elements
it contains, and exactly which types it contains at specific positions.
```ts twoslash
type StringNumberPair = [string, number];
// ^^^^^^^^^^^^^^^^
```ts twoslash
// @errors: 2493
function doSomething(pair: [string, number]) {
// ...
const c = pair[2];
}
console.log(inputString); // ^?
console.log(hash); // ^? }
<aside>
Tuple types are useful in heavily convention-based APIs, where each element's
meaning is "obvious".
This gives us flexibility in whatever we want to name our variables when we
destructure them.
In the above example, we were able to name elements `0` and `1` to whatever we
wanted.
However, since not every user holds the same view of what's obvious, it may be
worth reconsidering whether using objects with descriptive property names may be
better for your API.
</aside>
Other than those length checks, simple tuple types like these are equivalent to
types which are versions of `Array`s that declare properties for specific indexes,
and that declare `length` with a numeric literal type.
```ts twoslash
interface StringNumberPair {
// specialized properties
length: 2;
0: string;
1: number;
Another thing you may be interested in is that tuples can have optional
properties by writing out a question mark ( ? after an element's
type). Optional tuple elements can only come at the end, and also
affect the type of length .
```ts twoslash
type StringNumberBooleans = [string, number, ...boolean[]];
```ts twoslash
function foo(...args: [string, number, ...boolean[]]) {
var [x, y, ...z] = args;
// ...
}
<!--
TODO do we need this example?
For example, imagine we need to write a function that adds up `number`s based on
arguments that get passed in.
```ts twoslash
function sum(...args: number[]) {
// ...
}
We might feel like it makes little sense to take any fewer than 2
elements, so we want to require callers to provide at least 2
arguments. A first attempt might be
let result = 0;
for (const value of args) {
result += value;
}
return result;
}
-->
One final note about tuple types - tuples types have `readonly` variants, and can
be specified by sticking a `readonly` modifier in front of them - just like with
array shorthands.
```ts twoslash
function doSomething(pair: readonly [string, number]) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// ...
}
Tuples tend to be created and left un-modified in most code, so annotating types as
`readonly` tuples when possible is a good default.
This is also important given that array literals with `const` assertions will be
inferred with `readonly` tuple types.
```ts twoslash
// @errors: 2345
let point = [3, 4] as const;
distanceFromOrigin(point);
Method Syntax
Call Signatures
Construct Signatures
Index Signatures
Background reading: Classes (MDN)
This class isn't very useful yet, so let's start adding some members.
### Fields
```ts twoslash
// @strictPropertyInitialization: false
class Point {
x: number;
y: number;
}
Fields can also have initializers; these will run automatically when the
class is instantiated:
```ts twoslash
// @errors: 2322
class Point {
x = 0;
y = 0;
}
// ---cut---
const pt = new Point();
pt.x = "0";
--strictPropertyInitialization
```ts twoslash
class GoodGreeter {
name: string;
constructor() {
this.name = "hello";
}
}
### `readonly`
```ts twoslash
// @errors: 2540 2540
class Greeter {
readonly name: string = "world";
constructor(otherName?: string) {
if (otherName !== undefined) {
this.name = otherName;
}
}
err() {
this.name = "not ok";
}
}
const g = new Greeter();
g.name = "also not ok";
Constructors
Background Reading: Constructor (MDN)
Super Calls
Just as in JavaScript, if you have a base class, you'll need to call
super(); in your constructor body before using any this. members:
### Methods
```ts twoslash
class Point {
x = 10;
y = 10;
m() { // This is trying to modify 'x' from line 1, not the class
property x = "world"; } }
### Getters / Setters
```ts twoslash
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}
Index Signatures
Classes can declare index signatures; these work the same as [[Index
Signatures]] for other object types:
```ts twoslash class MyClass { [s: string]: boolean | ((s: string) =>
boolean); check(s: string) { return this[s] as boolean; } }
Because the index signature type needs to also capture the types of methods, it's
not easy to usefully use these types.
Generally it's better to store indexed data in another place instead of on the
class instance itself.
## Class Heritage
You can use an `implements` clause to check that a class satisfies a particular
`interface`.
An error will be issued if a class fails to correctly implement it:
```ts twoslash
// @errors: 2420
interface Pingable {
ping(): void;
}
B { .
Cautions
It's important to understand that an implements clause is only a check
that the class can be treated as the interface type. It doesn't change
the type of the class or its methods at all. A common source of error
is to assume that an implements clause will change the class type - it
doesn't!
In this example, we perhaps expected that `s`'s type would be influenced by the
`name: string` parameter of `check`.
It is not - `implements` clauses don't change how the class body is checked or its
type inferred.
```ts twoslash
// @errors: 2339
interface A {
x: number;
y?: number;
}
class C implements A {
x = 0;
}
const c = new C();
c.y = 10;
extends Clauses
Background Reading: extends keyword (MDN)
Classes may extend from a base class. A derived class has all the
properties and methods of its base class, and also define additional
members.
```ts twoslash class Animal { move() { console.log("Moving along!"); } }
TypeScript enforces that a derived class is always a subtype of its base class.
```ts twoslash
class Base {
greet() {
console.log("Hello, world!");
}
}
```ts twoslash
// @errors: 2416
class Base {
greet() {
console.log("Hello, world!");
}
}
If we compiled this code despite the error, this sample would then
crash:
```ts twoslash declare class Base { greet(): void; } declare class Derived
extends Base {} // ---cut--- const b: Base = new Derived(); //
Crashes because "name" will be undefined b.greet();
#### Initialization Order
The order that JavaScript classes initialize can be surprising in some cases.
Let's consider this code:
```ts twoslash
class Base {
name = "base";
constructor() {
console.log("My name is " + this.name);
}
}
This means that the base class constructor saw its own value for name
during its own constructor, because the derived class field initializations
hadn't run yet.
As a recommendation, you can manually adjust the prototype immediately after any
`super(...)` calls.
```ts twoslash
class FooError extends Error {
constructor(m: string) {
super(m);
sayHello() {
return "hello " + this.message;
}
}
However, any subclass of FooError will have to manually set the
prototype as well. For runtimes that don't support
Object.setPrototypeOf , you may instead be able to use __proto__ .
public
The default visibility of class members is public . A public member
can be accessed by anywhere:
### `protected`
`protected` members are only visible to subclasses of the class they're declared
in.
```ts twoslash
// @errors: 2445
class Greeter {
public greet() {
console.log("Hello, " + this.getName());
}
protected getName() {
return "hi";
}
}
Different OOP languages disagree about whether it's legal to access a `protected`
member through a base class reference:
```ts twoslash
// @errors: 2446
class Base {
protected x: number = 1;
}
class Derived1 extends Base {
protected x: number = 5;
}
class Derived2 extends Base {
f1(other: Derived2) {
other.x = 10;
}
f2(other: Base) {
other.x = 10;
}
}
private
private is like protected , but doesn't allow access to the member even
from subclasses:
```ts twoslash
// @errors: 2341
class Base {
private x = 0;
}
// ---cut---
class Derived extends Base {
showX() {
// Can't access in subclasses
console.log(this.x);
}
}
Different OOP languages disagree about whether different instances of the same
class may access each others' `private` members.
While languages like Java, C#, C++, Swift, and PHP allow this, Ruby does not.
```ts twoslash
class A {
private x = 10;
public sameAs(other: A) {
// No error
return other.x === this.x;
}
}
Caveats
Like other aspects of TypeScript's type system, private and protected
are only enforced during type checking. This means that JavaScript
runtime constructs like in or simple property lookup can still access a
private or protected member:
```js
// In a JavaScript file...
const s = new MySafe();
// Will print 12345
console.log(s.secretKey);
If you need to protect values in your class from malicious actors, you
should use mechanisms that offer hard runtime privacy, such as
closures, weak maps, or [[private fields]].
Static Members
Background Reading: Static Members (MDN)
Static members can also use the same `public`, `protected`, and `private`
visibility modifiers:
```ts twoslash
// @errors: 2341
class MyClass {
private static x = 0;
}
console.log(MyClass.x);
```ts twoslash
// @errors: 2699
class S {
static name = "S!";
}
Those constructs only exist because those languages force all data and
functions to be inside a class; because that restriction doesn't exist in
TypeScript, there's no need for them. A class with only a single
instance is typically just represented as a normal object in
JavaScript/TypeScript.
```ts twoslash
class Box<T> {
contents: T;
constructor(value: T) {
this.contents = value;
}
}
Classes can use generic constraints and defaults the same way as
interfaces.
It's important to remember that TypeScript doesn't change the runtime behavior of
JavaScript, and that JavaScript is somewhat famous for having some peculiar runtime
behaviors.
```ts twoslash
class MyClass {
name = "MyClass";
getName() {
return this.name;
}
}
const c = new MyClass();
const obj = {
name: "obj",
getName: c.getName,
};
This is rarely what you want to happen! TypeScript provides some ways
to mitigate or prevent this kind of error.
t
Arrow Functions
Background Reading: Arrow functions (MDN)
If you have a function that will often be called in a way that loses its
this context, it can make sense to use an arrow function property
instead of a method definition:
- The `this` value is guaranteed to be correct at runtime, even for code not
checked with TypeScript
- This will use more memory, because each class instance will have its own copy of
each function defined this way
- You can't use `super.getName` in a derived class, because there's no entry in the
prototype chain to fetch the base class method from
```ts twoslash
type SomeType = any;
// ---cut---
// TypeScript input with 'this' parameter
function fn(this: SomeType, x: number) {
/* ... */
}
// JavaScript output
function fn(x) {
/* ... */
}
TypeScript checks that calling a function with a this parameter is
done so with a correct context. Instead of using an arrow function,
we can add a this parameter to method definitions to statically
enforce that the method is called correctly:
This method takes the opposite trade-offs of the arrow function approach:
- JavaScript callers might still use the class method incorrectly without realizing
it
- Only one function per class definition gets allocated, rather than one per class
instance
- Base method definitions can still be called via `super.`
## `this` Types
In classes, a special type called `this` refers _dynamically_ to the type of the
current class.
Let's see how this is useful:
```ts twoslash
class Box {
content: string = "";
sameAs(other: this) {
return other.content === this.content;
}
}
TypeScript offers special syntax for turning a constructor parameter into a class
property with the same name and value.
These are called _parameter properties_ and are created by prefixing a constructor
argument with one of the visibility modifiers `public`, `private`, `protected`, or
`readonly`.
The resulting field gets those modifier(s):
```ts twoslash
// @errors: 2341
class A {
constructor(
public readonly x: number,
protected y: number,
private z: number
) {
// No body necessary
}
}
const a = new A(1, 2, 3);
console.log(a.x);
// ^?
console.log(a.z);
Class Expressions
Background reading: Class expressions (MDN)
Class expressions are very similar to class declarations. The only real
difference is that class expressions don't need a name, though we can
refer to them via whatever identifier they ended up bound to:
The role of abstract classes is to serve as a base class for subclasses which do
implement all the abstract members.
When a class doesn't have any abstract members, it is said to be _concrete_.
```ts twoslash
// @errors: 2511
abstract class Base {
abstract getName(): string;
printName() {
console.log("Hello, " + this.getName());
}
}
Notice that if we forget to implement the base class's abstract members, we'll get
an error:
```ts twoslash
// @errors: 2515
abstract class Base {
abstract getName(): string;
printName() {}
}
// ---cut---
class Derived extends Base {
// forgot to do anything
}
```ts twoslash
declare const greet: any, Base: any;
// ---cut---
// Bad!
greet(Base);
In most cases, classes in TypeScript are compared structurally, the same as other
types.
For example, these two classes can be used in place of each other because they're
identical:
```ts twoslash
class Point1 {
x = 0;
y = 0;
}
class Point2 {
x = 0;
y = 0;
}
// OK
const p: Point1 = new Point2();
```ts twoslash
class Empty {}
// All OK!
fn(window);
fn({});
fn(fn);
Modules as a concept in JavaScript have a long and complicated history
that makes any single definition or description difficult. Many competing
implementations have been shipped and adopted, and multiple mutually-
incompatible interop systems have been built on top of those.
Ultimately, this chapter requires much more background reading than
others.
No Modules
In the beginning, there were just <script> tags in HTML files. All
JavaScript code was loaded and executed before the page was
rendered, and all files worked in the same global scope. Different
parts of a program could talk to each other through global variables.
This was good for simple programs, but the limitations of this
approach are quickly encountered. Loading all JavaScript at once is bad
for page load times, and different files had to be careful to not
interfere with each other's global variables. Worse, there was no way
for a single JavaScript file to declare its dependencies. Programmers
had to ensure that any HTML file their file was included in had the
proper prerequisites.
AMD
// An example AMD module
define("my_module", ["dependency_1", "dependency_2"], function (dep1, dep2) {
return {
name: "My Awesome Module",
greet: () => {
alert("Hello, world!");
},
};
});
CommonJS
// An example CommonJS module
const fs = require("fs");
module.exports = function () {
return fs.readFile("someFile.txt");
};
UMD
// UMD wrapper for either global or AMD environment
// Adapted from https://github.com/umdjs/umd/blob/master/templates/amdWebGlobal.js
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(["b"], function (b) {
return (root.amdWebGlobal = factory(b));
});
} else {
root.amdWebGlobal = factory(root.b);
}
})(typeof self !== "undefined" ? self : this, function (b) {
// Use b in some fashion.
return {};
});
Some UMD libraries will always create a global variable, while others will
only do this if no module loader is present.
ES6
// An example ES6 module
import * as NS from "someModule";
import { prop1, prop2 } from "../anotherModule";
ES6
Background Reading: import and export declarations (MDN)
Namespace Imports
Namespace imports are treated as importing the entire module object:
// Namespace import
import * as ns from "m";
// Becomes (CommonJS)
const ns = require("m");
// Becomes (AMD)
define(["m"], function (ns) {});
This code does not run in a true ES6 environment. If the thing you're
trying to import is a function, you should either use a default import
or a CommonJS-style import (see below>) See the StackOverflow
question "What does “… resolves to a non-module entity and cannot be
imported using this construct” mean? for more details.
Destructuring Imports
Destructuring imports bind to properties of the module:
// Destructured import
import { prop } from "m";
prop.greet();
Default Imports
Default imports import the .default member of a module:
Export Forms
Export declarations follow the same pattern as imports -- when
targeting AMD or CommonJS, they create corresponding named
properties. Note that if you're writing a CommonJS module using ES6
syntax, you usually don't want to create a default export, as
CommonJS consumers won't be expecting to find a property with this
name.
TypeScript uses the same syntax for importing types and namespaces as it does for
values.
TODO finish
TypeScript allows you to write module-based code in a syntax that can be transpiled
to the module format of your choice.
## Non-modules
If a `.ts` file doesn't have any `import` or `export` declarations, the file will
automatically be considered a non-module file.
These files' variables are declared in the global scope, and it's assumed that
you'll either use the `--outFile` compiler option to join multiple input files into
one output file, or use multiple `<script>` tags in your HTML to load these files
(in the correct order!).
If you have a file that doesn't currently have any imports or exports, but you want
to be treated as a module, add the line
```ts twoslash
export {};
ES6
AMD
Import Paths and Module
Resolution
Declaring Modules
A "module" in modern JavaScript parlance
Modules are executed within their own scope, not in the global scope.
This means that variables, functions, classes, etc. declared in a module
are not visible outside the module unless they are explicitly exported
using one of the export forms. Conversely, to consume a variable,
function, class, interface, etc. exported from a different module, it
has to be imported using one of the import forms.
d l
ES Modules
Background Reading: ES Modules: A cartoon deep-dive
Import Forms
-
The keyof type operator
The keyof operator takes an object type and produces a string or
numeric literal union of its keys:
If the type has a `string` or `number` index signature, `keyof` will return those
types instead:
```ts twoslash
type Arrayish = { [n: number]: unknown };
type A = keyof Arrayish;
// ^?
TypeScript adds a `typeof` operator you can use in a _type_ context to refer to the
_type_ of a variable or property:
```ts twoslash
let s = "hello";
let n: typeof s;
// ^?
This isn't very useful for basic types, but combined with other type
operators, you can use typeof to conveniently express many patterns.
For an example, let's start by looking at the predefined type
ReturnType<T> . It takes a function type and produces its return type:
```ts twoslash
// @errors: 2749
function f() {
return { x: 10, y: 3 };
}
type P = ReturnType<f>;
Remember that values and types aren't the same thing. To refer to
the type that the value f has, we use typeof :
```ts twoslash function f() { return { x: 10, y: 3 }; } type P =
ReturnType; // ^?
### Limitations
TypeScript intentionally limits the sorts of expressions you can use `typeof` on.
Specifically, it's only legal to use `typeof` on identifiers (i.e. variable names)
or their properties.
This helps avoid the confusing trap of writing code you think is executing, but
isn't:
```ts twoslash
// @errors: 1005
declare const msgbox: () => boolean;
// type msgbox = any;
// ---cut---
// Meant to use =
let shouldContinue: typeof msgbox("Are you sure you want to continue?");
We can use an indexed access type to look up a specific property on
another type:
```ts twoslash type Person = { age: number; name: string; alive: boolean
}; type A = Person["age"]; // ^?
The indexing type is itself a type, so we can use unions, `keyof`, or other types
entirely:
```ts twoslash
type Person = { age: number; name: string; alive: boolean };
// ---cut---
type I1 = Person["age" | "name"];
// ^?
You'll even see an error if you try to index a property that doesn't
exist:
```ts twoslash
const MyArray = [
{ name: "Alice", age: 15 },
{ name: "Bob", age: 23 },
{ name: "Eve", age: 38 },
];
Conditional types take a form that looks a little like conditional expresions
(`cond ? trueExpression : falseExpression`) in JavaScript:
```ts twoslash
type SomeType = any;
type OtherType = any;
type TrueType = any;
type FalseType = any;
type Stuff =
// ---cut---
SomeType extends OtherType ? TrueType : FalseType;
When the type on the left of the extends is assignable to the one on
the right, then you'll get the type in the first branch (the "true"
branch); otherwise you'll get the type in the latter branch (the "false"
branch).
From the examples above, conditional types might not immediately seem
useful - we can tell ourselves whether or not Dog extends Animal and
pick number or string ! But the power of conditional types comes from
using them with generics.
These overloads for createLabel describe a single JavaScript function that makes a
choice based on the types of its inputs. Note a few things:
1. If a library has to make the same sort of choice over and over throughout its
API, this becomes cumbersome.
2. We have to create three overloads: one for each case when we're _sure_ of the
type (one for `string` and one for `number`), and one for the most general case
(taking a `string | number`). For every new type `createLabel` can handle, the
number of overloads grows exponentially.
```ts twoslash
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}
// ---cut---
type NameOrId<T extends number | string> = T extends number
? IdLabel
: NameLabel;
We can then use that conditional type to simplify out overloads down
to a single function with no overloads.
let a = createLabel("typescript"); // ^?
let b = createLabel(2.8); // ^?
Often, the checks in a conditional type will provide us with some new information.
Just like with narrowing with type guards can give us a more specific type, the
true branch of a conditional type will further constraint generics by the type we
check against.
```ts twoslash
// @errors: 2536
type MessageOf<T> = T["message"];
```ts twoslash
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
Within the true branch, TypeScript knows that T will have a message
property.
We just found ourselves using conditional types to apply constraints and then
extract out types.
This ends up being such a common operation that conditional types make it easier.
Conditional types provide us with a way to infer from types we compare against in
the true branch using the `infer` keyword.
For example, we could have inferred the element type in `Flatten` instead of
fetching it out "manually" with an indexed access type:
```ts twoslash
type Flatten<T> = T extends Array<infer U> ? U : T;
We can write some useful helper type aliases using the infer keyword.
For example, for simple cases, we can extract the return type out
from function types:
When conditional types act on a generic type, they become _distributive_ when given
a union type.
For example, take the following:
```ts twoslash
type Foo<T> = T extends any ? T[] : never;
If we plug a union type into Foo , then the conditional type will be
applied to each member of that union.
```ts twoslash
type Blah =
// ---cut---
string | number;
and maps over each member type of the union, to what is effectively
```ts twoslash type Foo = T extends any ? T[] : never; type Blah = //
---cut--- Foo | Foo;
```ts twoslash
type Blah =
// ---cut---
string[] | number[];