Compiler Options
Top Level
"compilerOptions"
Type Checking
allowUnreachableCode,
allowUnusedLabels,
alwaysStrict,
exactOptionalPropertyTypes,
noFallthroughCasesInSwitch,
noImplicitAny,
noImplicitOverride,
noImplicitReturns,
noImplicitThis,
noPropertyAccessFromIndexSignature,
noUncheckedIndexedAccess,
noUnusedLocals,
noUnusedParameters,
strict,
strictBindCallApply,
strictBuiltinIteratorReturn,
strictFunctionTypes,
strictNullChecks,
strictPropertyInitialization and
useUnknownInCatchVariables
Modules
allowArbitraryExtensions,
allowImportingTsExtensions,
allowUmdGlobalAccess,
baseUrl,
customConditions,
module,
moduleResolution,
moduleSuffixes,
noResolve,
noUncheckedSideEffectImports,
paths,
resolveJsonModule,
resolvePackageJsonExports,
resolvePackageJsonImports,
rewriteRelativeImportExtensions,
rootDir,
rootDirs,
typeRoots and
types
Emit
JavaScript Support
Editor Support
Interop Constraints
Backwards Compatibility
Language and Environment
Compiler Diagnostics
Projects
Output Formatting
Completeness
Command Line
Watch Options
"typeAcquisition"
typeAcquisition
Root Fields
Starting up are the root options in the TSConfig - these options relate to how your TypeScript or JavaScript project is set up.
# Files -files
Specifies an allowlist of files to include in the program. An error occurs if any of the files can’t be found.
{" ": {}," ": ["core.ts","sys.ts","types.ts","scanner.ts","parser.ts","utilities.ts","binder.ts","checker.ts","tsc.ts"]}
This is useful when you only have a small number of files and don’t need to use a glob to reference many files.If you need that then useinclude
.
# Extends -extends
The value ofextends
is a string which contains a path to another configuration file to inherit from.The path may use Node.js style resolution.
The configuration from the base file are loaded first, then overridden by those in the inheriting config file. All relative paths found in the configuration file will be resolved relative to the configuration file they originated in.
It’s worth noting thatfiles
,include
, andexclude
from the inheriting config fileoverwrite those from thebase config file, and that circularity between configuration files is not allowed.
Currently, the only top-level property that is excluded from inheritance isreferences
.
Example
configs/base.json
:
{" ": {" ":true," ":true}}
tsconfig.json
:
{" ":"./configs/base"," ": ["main.ts","supplemental.ts"]}
tsconfig.nostrictnull.json
:
{" ":"./tsconfig"," ": {" ":false}}
Properties with relative paths found in the configuration file, which aren’t excluded from inheritance, will be resolved relative to the configuration file they originated in.
- Default:
false
- Released:
# Include -include
Specifies an array of filenames or patterns to include in the program.These filenames are resolved relative to the directory containing thetsconfig.json
file.
json
{"include": ["src/**/*","tests/**/*"]}
Which would include:
.├── scripts ⨯│ ├── lint.ts ⨯│ ├── update_deps.ts ⨯│ └── utils.ts ⨯├── src ✓│ ├── client ✓│ │ ├── index.ts ✓│ │ └── utils.ts ✓│ ├── server ✓│ │ └── index.ts ✓├── tests ✓│ ├── app.test.ts ✓│ ├── utils.ts ✓│ └── tests.d.ts ✓├── package.json├── tsconfig.json└── yarn.lock
include
andexclude
support wildcard characters to make glob patterns:
*
matches zero or more characters (excluding directory separators)?
matches any one character (excluding directory separators)**/
matches any directory nested to any level
If the last path segment in a pattern does not contain a file extension or wildcard character, then it is treated as a directory, and files with supported extensions inside that directory are included (e.g..ts
,.tsx
, and.d.ts
by default, with.js
and.jsx
ifallowJs
is set to true).
# Exclude -exclude
Specifies an array of filenames or patterns that should be skipped when resolvinginclude
.
Important:exclude
only changes which files are included as a result of theinclude
setting.A file specified byexclude
can still become part of your codebase due to animport
statement in your code, atypes
inclusion, a/// <reference
directive, or being specified in thefiles
list.
It is not a mechanism thatprevents a file from being included in the codebase - it simply changes what theinclude
setting finds.
# References -references
Project references are a way to structure your TypeScript programs into smaller pieces.Using Project References can greatly improve build and editor interaction times, enforce logical separation between components, and organize your code in new and improved ways.
You can read more about how references works in theProject References section of the handbook
- Default:
false
- Released:
Compiler Options
These options make up the bulk of TypeScript’s configuration and it covers how the language should work.
#Type Checking
# Allow Unreachable Code -allowUnreachableCode
When:
undefined
(default) provide suggestions as warnings to editorstrue
unreachable code is ignoredfalse
raises compiler errors about unreachable code
These warnings are only about code which is provably unreachable due to the use of JavaScript syntax, for example:
ts
functionfn(n:number) {if (n >5) {returntrue;}else {returnfalse;}returntrue;}
With"allowUnreachableCode": false
:
tsTry
functionfn (n :number) {if (n >5) {returntrue;}else {returnfalse;}returntrue;Unreachable code detected.7027Unreachable code detected.}
This does not affect errors on the basis of code whichappears to be unreachable due to type analysis.
- Released:
# Allow Unused Labels -allowUnusedLabels
When:
undefined
(default) provide suggestions as warnings to editorstrue
unused labels are ignoredfalse
raises compiler errors about unused labels
Labels are very rare in JavaScript and typically indicate an attempt to write an object literal:
tsTry
functionverifyAge (age :number) {// Forgot 'return' statementif (age >18) {Unused label.7028Unused label.verified :true;}}
- Released:
# Always Strict -alwaysStrict
Ensures that your files are parsed in the ECMAScript strict mode, and emit “use strict” for each source file.
ECMAScript strict mode was introduced in ES5 and provides behavior tweaks to the runtime of the JavaScript engine to improve performance, and makes a set of errors throw instead of silently ignoring them.
# Exact Optional Property Types -exactOptionalPropertyTypes
With exactOptionalPropertyTypes enabled, TypeScript applies stricter rules around how it handles properties ontype
orinterfaces
which have a?
prefix.
For example, this interface declares that there is a property which can be one of two strings: ‘dark’ or ‘light’ or it should not be in the object.
ts
interfaceUserDefaults {// The absence of a value represents 'system'colorThemeOverride?:"dark" |"light";}
Without this flag enabled, there are three values which you can setcolorThemeOverride
to be: “dark”, “light” andundefined
.
Setting the value toundefined
will allow most JavaScript runtime checks for the existence to fail, which is effectively falsy. However, this isn’t quite accurate;colorThemeOverride: undefined
is not the same ascolorThemeOverride
not being defined. For example,"colorThemeOverride" in settings
would have different behavior withundefined
as the key compared to not being defined.
exactOptionalPropertyTypes
makes TypeScript truly enforce the definition provided as an optional property:
tsTry
constsettings =getUserSettings ();settings .colorThemeOverride ="dark";settings .colorThemeOverride ="light";// But not:Type 'undefined' is not assignable to type '"dark" | "light"' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.2412Type 'undefined' is not assignable to type '"dark" | "light"' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.settings .colorThemeOverride =undefined ;
- Recommended
- Released:
# No Fallthrough Cases In Switch -noFallthroughCasesInSwitch
Report errors for fallthrough cases in switch statements.Ensures that any non-empty case inside a switch statement includes eitherbreak
,return
, orthrow
.This means you won’t accidentally ship a case fallthrough bug.
tsTry
consta :number =6;switch (a ) {case0:Fallthrough case in switch.7029Fallthrough case in switch.console .log ("even");case1:console .log ("odd");break;}
- Released:
# No Implicit Any -noImplicitAny
In some cases where no type annotations are present, TypeScript will fall back to a type ofany
for a variable when it cannot infer the type.
This can cause some errors to be missed, for example:
tsTry
functionfn (s ) {// No error?console .log (s .subtr (3));}fn (42);
Turning onnoImplicitAny
however TypeScript will issue an error whenever it would have inferredany
:
tsTry
functionParameter 's' implicitly has an 'any' type.7006Parameter 's' implicitly has an 'any' type.fn () { s console .log (s .subtr (3));}
# No Implicit Override -noImplicitOverride
When working with classes which use inheritance, it’s possible for a sub-class to get “out of sync” with the functions it overloads when they are renamed in the base class.
For example, imagine you are modeling a music album syncing system:
tsTry
classAlbum {download () {// Default behavior}}classSharedAlbum extendsAlbum {download () {// Override to get info from many sources}}
Then when you add support for machine-learning generated playlists, you refactor theAlbum
class to have a ‘setup’ function instead:
tsTry
classAlbum {setup () {// Default behavior}}classMLAlbum extendsAlbum {setup () {// Override to get info from algorithm}}classSharedAlbum extendsAlbum {download () {// Override to get info from many sources}}
In this case, TypeScript has provided no warning thatdownload
onSharedAlbum
expected to override a function in the base class.
UsingnoImplicitOverride
you can ensure that the sub-classes never go out of sync, by ensuring that functions which override include the keywordoverride
.
The following example hasnoImplicitOverride
enabled, and you can see the error received whenoverride
is missing:
tsTry
classAlbum {setup () {}}classMLAlbum extendsAlbum {overridesetup () {}}classSharedAlbum extendsAlbum {This member must have an 'override' modifier because it overrides a member in the base class 'Album'.4114This member must have an 'override' modifier because it overrides a member in the base class 'Album'.() {} setup }
- Released:
# No Implicit Returns -noImplicitReturns
When enabled, TypeScript will check all code paths in a function to ensure they return a value.
tsTry
functionFunction lacks ending return statement and return type does not include 'undefined'.2366Function lacks ending return statement and return type does not include 'undefined'.lookupHeadphonesManufacturer (color :"blue" |"black"):string {if (color ==="blue") {return"beats";}else {("bose");}}
- Released:
# No Implicit This -noImplicitThis
Raise error on ‘this’ expressions with an implied ‘any’ type.
For example, the class below returns a function which tries to accessthis.width
andthis.height
– but the contextforthis
inside the function insidegetAreaFunction
is not the instance of the Rectangle.
tsTry
classRectangle {width :number;height :number;constructor(width :number,height :number) {this.width =width ;this.height =height ;}getAreaFunction () {returnfunction () {return'this' implicitly has type 'any' because it does not have a type annotation.'this' implicitly has type 'any' because it does not have a type annotation.2683this .width *this .height ;
2683'this' implicitly has type 'any' because it does not have a type annotation.'this' implicitly has type 'any' because it does not have a type annotation.};}}
# No Property Access From Index Signature -noPropertyAccessFromIndexSignature
This setting ensures consistency between accessing a field via the “dot” (obj.key
) syntax, and “indexed” (obj["key"]
) and the way which the property is declared in the type.
Without this flag, TypeScript will allow you to use the dot syntax to access fields which are not defined:
tsTry
interfaceGameSettings {// Known up-front propertiesspeed :"fast" |"medium" |"slow";quality :"high" |"low";// Assume anything unknown to the interface// is a string.[key :string]:string;}constsettings =getSettings ();settings .speed ;settings .quality ;// Unknown key accessors are allowed on// this object, and are `string`settings .username ;
Turning the flag on will raise an error because the unknown field uses dot syntax instead of indexed syntax.
tsTry
constsettings =getSettings ();settings .speed ;settings .quality ;// This would need to be settings["username"];Property 'username' comes from an index signature, so it must be accessed with ['username'].4111Property 'username' comes from an index signature, so it must be accessed with ['username'].settings .; username
The goal of this flag is to signal intent in your calling syntax about how certain you are this property exists.
- Released:
# No Unchecked Indexed Access -noUncheckedIndexedAccess
TypeScript has a way to describe objects which have unknown keys but known values on an object, via index signatures.
tsTry
interfaceEnvironmentVars {NAME :string;OS :string;// Unknown properties are covered by this index signature.[propName :string]:string;}declareconstenv :EnvironmentVars ;// Declared as existingconstsysName =env .NAME ;constos =env .OS ;// Not declared, but because of the index// signature, then it is considered a stringconstnodeEnv =env .NODE_ENV ;
Turning onnoUncheckedIndexedAccess
will addundefined
to any un-declared field in the type.
tsTry
declareconstenv :EnvironmentVars ;// Declared as existingconstsysName =env .NAME ;constos =env .OS ;// Not declared, but because of the index// signature, then it is considered a stringconstnodeEnv =env .NODE_ENV ;
- Released:
# No Unused Locals -noUnusedLocals
# No Unused Parameters -noUnusedParameters
# Strict -strict
Thestrict
flag enables a wide range of type checking behavior that results in stronger guarantees of program correctness.Turning this on is equivalent to enabling all of thestrict mode family options, which are outlined below.You can then turn off individual strict mode family checks as needed.
Future versions of TypeScript may introduce additional stricter checking under this flag, so upgrades of TypeScript might result in new type errors in your program.When appropriate and possible, a corresponding flag will be added to disable that behavior.
# Strict Bind Call Apply -strictBindCallApply
When set, TypeScript will check that the built-in methods of functionscall
,bind
, andapply
are invoked with correct argument for the underlying function:
tsTry
// With strictBindCallApply onfunctionfn (x :string) {returnparseInt (x );}constn1 =fn .call (undefined ,"10");constArgument of type 'boolean' is not assignable to parameter of type 'string'.2345Argument of type 'boolean' is not assignable to parameter of type 'string'.n2 =fn .call (undefined ,false );
Otherwise, these functions accept any arguments and will returnany
:
tsTry
// With strictBindCallApply offfunctionfn (x :string) {returnparseInt (x );}// Note: No error; return type is 'any'constn =fn .call (undefined ,false);
# strictBuiltinIteratorReturn -strictBuiltinIteratorReturn
# Strict Function Types -strictFunctionTypes
When enabled, this flag causes functions parameters to be checked more correctly.
Here’s a basic example withstrictFunctionTypes
off:
tsTry
functionfn (x :string) {console .log ("Hello, " +x .toLowerCase ());}typeStringOrNumberFunc = (ns :string |number)=>void;// Unsafe assignmentletfunc :StringOrNumberFunc =fn ;// Unsafe call - will crashfunc (10);
WithstrictFunctionTypes
on, the error is correctly detected:
tsTry
functionfn (x :string) {console .log ("Hello, " +x .toLowerCase ());}typeStringOrNumberFunc = (ns :string |number)=>void;// Unsafe assignment is preventedletType '(x: string) => void' is not assignable to type 'StringOrNumberFunc'. Types of parameters 'x' and 'ns' are incompatible. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.2322Type '(x: string) => void' is not assignable to type 'StringOrNumberFunc'. Types of parameters 'x' and 'ns' are incompatible. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.: func StringOrNumberFunc =fn ;
During development of this feature, we discovered a large number of inherently unsafe class hierarchies, including some in the DOM.Because of this, the setting only applies to functions written infunction syntax, not to those inmethod syntax:
tsTry
typeMethodish = {func (x :string |number):void;};functionfn (x :string) {console .log ("Hello, " +x .toLowerCase ());}// Ultimately an unsafe assignment, but not detectedconstm :Methodish = {func :fn ,};m .func (10);
# Strict Null Checks -strictNullChecks
WhenstrictNullChecks
isfalse
,null
andundefined
are effectively ignored by the language.This can lead to unexpected errors at runtime.
WhenstrictNullChecks
istrue
,null
andundefined
have their own distinct types and you’ll get a type error if you try to use them where a concrete value is expected.
For example with this TypeScript code,users.find
has no guarantee that it will actually find a user, but you canwrite code as though it will:
tsTry
declareconstloggedInUsername :string;constusers = [{name :"Oby",age :12 },{name :"Heera",age :32 },];constloggedInUser =users .find ((u )=>u .name ===loggedInUsername );console .log (loggedInUser .age );
SettingstrictNullChecks
totrue
will raise an error that you have not made a guarantee that theloggedInUser
exists before trying to use it.
tsTry
declareconstloggedInUsername :string;constusers = [{name :"Oby",age :12 },{name :"Heera",age :32 },];constloggedInUser =users .find ((u )=>u .name ===loggedInUsername );'loggedInUser' is possibly 'undefined'.18048'loggedInUser' is possibly 'undefined'.console .log (. loggedInUser age );
The second example failed because the array’sfind
function looks a bit like this simplification:
ts
// When strictNullChecks: truetypeArray = {find(predicate: (value:any,index:number)=>boolean):S |undefined;};// When strictNullChecks: false the undefined is removed from the type system,// allowing you to write code which assumes it always found a resulttypeArray = {find(predicate: (value:any,index:number)=>boolean):S;};
# Strict Property Initialization -strictPropertyInitialization
When set to true, TypeScript will raise an error when a class property was declared but not set in the constructor.
tsTry
classUserAccount {name :string;accountType ="user";Property 'email' has no initializer and is not definitely assigned in the constructor.2564Property 'email' has no initializer and is not definitely assigned in the constructor.:string; address :string |undefined;constructor(name :string) {this.name =name ;// Note that this.email is not set}}
In the above case:
this.name
is set specifically.this.accountType
is set by default.this.email
is not set and raises an error.this.address
is declared as potentiallyundefined
which means it does not have to be set.
# Use Unknown In Catch Variables -useUnknownInCatchVariables
In TypeScript 4.0, support was added to allow changing the type of the variable in a catch clause fromany
tounknown
. Allowing for code like:
tsTry
try {// ...}catch (err :unknown) {// We have to verify err is an// error before using it as one.if (err instanceofError ) {console .log (err .message );}}
This pattern ensures that error handling code becomes more comprehensive because you cannot guarantee that the object being thrownis a Error subclass ahead of time. With the flaguseUnknownInCatchVariables
enabled, then you do not need the additional syntax (: unknown
) nor a linter rule to try enforce this behavior.
#Modules
# Allow Arbitrary Extensions -allowArbitraryExtensions
In TypeScript 5.0, when an import path ends in an extension that isn’t a known JavaScript or TypeScript file extension, the compiler will look for a declaration file for that path in the form of{file basename}.d.{extension}.ts
.For example, if you are using a CSS loader in a bundler project, you might want to write (or generate) declaration files for those stylesheets:
css
/* app.css */.cookie-banner {display:none;}
ts
// app.d.css.tsdeclareconstcss: {cookieBanner:string;};exportdefaultcss;
ts
// App.tsximportstylesfrom"./app.css";styles.cookieBanner;// string
By default, this import will raise an error to let you know that TypeScript doesn’t understand this file type and your runtime might not support importing it.But if you’ve configured your runtime or bundler to handle it, you can suppress the error with the new--allowArbitraryExtensions
compiler option.
Note that historically, a similar effect has often been achievable by adding a declaration file namedapp.css.d.ts
instead ofapp.d.css.ts
- however, this just worked through Node’srequire
resolution rules for CommonJS.Strictly speaking, the former is interpreted as a declaration file for a JavaScript file namedapp.css.js
.Because relative files imports need to include extensions in Node’s ESM support, TypeScript would error on our example in an ESM file under--moduleResolution node16
ornodenext
.
For more information, read upthe proposal for this feature andits corresponding pull request.
- Released:
# Allow Importing TS Extensions -allowImportingTsExtensions
--allowImportingTsExtensions
allows TypeScript files to import each other with a TypeScript-specific extension like.ts
,.mts
, or.tsx
.
This flag is only allowed when--noEmit
or--emitDeclarationOnly
is enabled, since these import paths would not be resolvable at runtime in JavaScript output files.The expectation here is that your resolver (e.g. your bundler, a runtime, or some other tool) is going to make these imports between.ts
files work.
- Default:
true
ifrewriteRelativeImportExtensions
;false
otherwise. - Released:
# Allow Umd Global Access -allowUmdGlobalAccess
When set to true,allowUmdGlobalAccess
lets you access UMD exports as globals from inside module files. A module file is a file that has imports and/or exports. Without this flag, using an export from a UMD module requires an import declaration.
An example use case for this flag would be a web project where you know the particular library (like jQuery or Lodash) will always be available at runtime, but you can’t access it with an import.
- Released:
# Base URL -baseUrl
Sets a base directory from which to resolve bare specifier module names. For example, in the directory structure:
project├── ex.ts├── hello│ └── world.ts└── tsconfig.json
With"baseUrl": "./"
, TypeScript will look for files starting at the same folder as thetsconfig.json
:
ts
import {helloWorld }from"hello/world";console.log(helloWorld);
This resolution has higher priority than lookups fromnode_modules
.
This feature was designed for use in conjunction with AMD module loaders in the browser, and is not recommended in any other context. As of TypeScript 4.1,baseUrl
is no longer required to be set when usingpaths
.
- Released:
# Custom Conditions -customConditions
--customConditions
takes a list of additionalconditions that should succeed when TypeScript resolves from anexports
orimports
field of apackage.json
.These conditions are added to whatever existing conditions a resolver will use by default.
For example, when this field is set in atsconfig.json
as so:
jsonc
{"compilerOptions": {"target":"es2022","moduleResolution":"bundler","customConditions": ["my-condition"]}}
Any time anexports
orimports
field is referenced inpackage.json
, TypeScript will consider conditions calledmy-condition
.
So when importing from a package with the followingpackage.json
jsonc
{// ..."exports": {".": {"my-condition":"./foo.mjs","node":"./bar.mjs","import":"./baz.mjs","require":"./biz.mjs"}}}
TypeScript will try to look for files corresponding tofoo.mjs
.
This field is only valid under thenode16
,nodenext
, andbundler
options for--moduleResolution
.
# Module -module
Sets the module system for the program. See thetheory behind TypeScript’smodule
option andits reference page for more information. You very likely want"nodenext"
for modern Node.js projects andpreserve
oresnext
for code that will be bundled.
Changingmodule
affectsmoduleResolution
whichalso has a reference page.
Here’s some example output for this file:
tsTry
// @filename: index.tsimport {valueOfPi }from"./constants";exportconsttwoPi =valueOfPi *2;
CommonJS
tsTry
"use strict";Object.defineProperty(exports,"__esModule", {value:true });exports.twoPi =void0;constconstants_1 =require("./constants");exports.twoPi =constants_1.valueOfPi *2;
UMD
tsTry
(function (factory) {if (typeofmodule ==="object" &&typeofmodule.exports ==="object") {varv =factory(require,exports);if (v !==undefined)module.exports =v;}elseif (typeofdefine ==="function" &&define.amd) {define(["require","exports","./constants"],factory);}})(function (require,exports) {"use strict";Object.defineProperty(exports,"__esModule", {value:true });exports.twoPi =void0;constconstants_1 =require("./constants");exports.twoPi =constants_1.valueOfPi *2;});
AMD
tsTry
define(["require","exports","./constants"],function (require,exports,constants_1) {"use strict";Object.defineProperty(exports,"__esModule", {value:true });exports.twoPi =void0;exports.twoPi =constants_1.valueOfPi *2;});
System
tsTry
System.register(["./constants"],function (exports_1,context_1) {"use strict";varconstants_1,twoPi;var__moduleName =context_1 &&context_1.id;return {setters: [function (constants_1_1) {constants_1 =constants_1_1;}],execute:function () {exports_1("twoPi",twoPi =constants_1.valueOfPi *2);}};});
ESNext
tsTry
import {valueOfPi }from"./constants";exportconsttwoPi =valueOfPi *2;
ES2015
/ES6
/ES2020
/ES2022
tsTry
import {valueOfPi }from"./constants";exportconsttwoPi =valueOfPi *2;
In addition to the base functionality ofES2015
/ES6
,ES2020
adds support fordynamicimport
s, andimport.meta
whileES2022
further adds support fortop levelawait
.
node16
/node18
/nodenext
Thenode16
,node18
, andnodenext
modes integrate with Node’snative ECMAScript Module support. The emitted JavaScript uses eitherCommonJS
orES2020
output depending on the file extension and the value of thetype
setting in the nearestpackage.json
. Module resolution also works differently. You can learn more in thehandbook andModules Reference.
node16
is available from TypeScript 4.7node18
is available from TypeScript 5.8 as a replacement fornode16
, with added support for import attributes.nodenext
is available from TypeScript 4.7, but its behavior changes with the latest stable versions of Node.js. As of TypeScript 5.8,nodenext
supportsrequire
of ECMAScript modules.
preserve
In--module preserve
(added in TypeScript 5.4), ECMAScript imports and exports written in input files are preserved in the output, and CommonJS-styleimport x = require("...")
andexport = ...
statements are emitted as CommonJSrequire
andmodule.exports
. In other words, the format of each individual import or export statement is preserved, rather than being coerced into a single format for the whole compilation (or even a whole file).
tsTry
import {valueOfPi }from"./constants";constconstants =require("./constants");exportconstpiSquared =valueOfPi *constants.valueOfPi;
While it’s rare to need to mix imports and require calls in the same file, thismodule
mode best reflects the capabilities of most modern bundlers, as well as the Bun runtime.
Why care about TypeScript’s
module
emit with a bundler or with Bun, where you’re likely also settingnoEmit
? TypeScript’s type checking and module resolution behavior are affected by the module format that itwould emit. Settingmodule
gives TypeScript information about how your bundler or runtime will process imports and exports, which ensures that the types you see on imported values accurately reflect what will happen at runtime or after bundling.
None
tsTry
"use strict";Object.defineProperty(exports,"__esModule", {value:true });exports.twoPi =void0;constconstants_1 =require("./constants");exports.twoPi =constants_1.valueOfPi *2;
- Default:
CommonJS
iftarget
isES5
;ES6
/ES2015
otherwise. - Allowed:
none
commonjs
amd
umd
system
es6
/es2015
es2020
es2022
esnext
node16
node18
nodenext
preserve
- Related:
- Released:
# Module Resolution -moduleResolution
Specify the module resolution strategy:
'node16'
or'nodenext'
for modern versions of Node.js. Node.js v12 and later supports both ECMAScript imports and CommonJSrequire
, which resolve using different algorithms. ThesemoduleResolution
values, when combined with the correspondingmodule
values, picks the right algorithm for each resolution based on whether Node.js will see animport
orrequire
in the output JavaScript code.'node10'
(previously called'node'
) for Node.js versions older than v10, which only support CommonJSrequire
. You probably won’t need to usenode10
in modern code.'bundler'
for use with bundlers. Likenode16
andnodenext
, this mode supports package.json"imports"
and"exports"
, but unlike the Node.js resolution modes,bundler
never requires file extensions on relative paths in imports.'classic'
was used in TypeScript before the release of 1.6.classic
should not be used.
There are reference pages explaining thetheory behind TypeScript’s module resolution and thedetails of each option.
# Module Suffixes -moduleSuffixes
Provides a way to override the default list of file name suffixes to search when resolving a module.
{" ": {" ": [".ios",".native",""]}}
Given the above configuration, an import like the following:
ts
import*asfoofrom"./foo";
TypeScript will look for the relative files./foo.ios.ts
,./foo.native.ts
, and finally./foo.ts
.
Note the empty string""
inmoduleSuffixes
which is necessary for TypeScript to also look-up./foo.ts
.
This feature can be useful for React Native projects where each target platform can use a separate tsconfig.json with differingmoduleSuffixes
.
- Released:
# No Resolve -noResolve
By default, TypeScript will examine the initial set of files forimport
and<reference
directives and add these resolved files to your program.
IfnoResolve
is set, this process doesn’t happen.However,import
statements are still checked to see if they resolve to a valid module, so you’ll need to make sure this is satisfied by some other means.
- Released:
# noUncheckedSideEffectImports -noUncheckedSideEffectImports
In JavaScript it’s possible toimport
a module without actually importing any values from it.
ts
import"some-module";
These imports are often calledside effect imports because the only useful behavior they can provide is by executing some side effect (like registering a global variable, or adding a polyfill to a prototype).
By default, TypeScript will not check these imports for validity. If the import resolves to a valid source file, TypeScript will load and check the file.If no source file is found, TypeScript will silently ignore the import.
This is surprising behavior, but it partially stems from modeling patterns in the JavaScript ecosystem.For example, this syntax has also been used with special loaders in bundlers to load CSS or other assets.Your bundler might be configured in such a way where you can include specific.css
files by writing something like the following:
tsx
import"./button-component.css";exportfunctionButton() {// ...}
Still, this masks potential typos on side effect imports.
When--noUncheckedSideEffectImports
is enabled, TypeScript will error if it can’t find a source file for a side effect import.
ts
import"oops-this-module-does-not-exist";// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// error: Cannot find module 'oops-this-module-does-not-exist' or its corresponding// type declarations.
When enabling this option, some working code may now receive an error, like in the CSS example above.To work around this, users who want to just write side effectimport
s for assets might be better served by writing what’s called anambient module declaration with a wildcard specifier.It would go in a global file and look something like the following:
ts
// ./src/globals.d.ts// Recognize all CSS files as module imports.declaremodule"*.css" {}
In fact, you might already have a file like this in your project!For example, running something likevite init
might create a similarvite-env.d.ts
.
- Released:
# Paths -paths
A series of entries which re-map imports to lookup locations relative to thebaseUrl
if set, or to the tsconfig file itself otherwise. There is a larger coverage ofpaths
inthemoduleResolution
reference page.
paths
lets you declare how TypeScript should resolve an import in yourrequire
/import
s.
{" ": {" ": {"jquery": ["./vendor/jquery/dist/jquery"]}}}
This would allow you to be able to writeimport "jquery"
, and get all of the correct typing locally.
{" ": {" ": {"app/*": ["./src/app/*"],"config/*": ["./src/app/_config/*"],"environment/*": ["./src/environments/*"],"shared/*": ["./src/app/_shared/*"],"helpers/*": ["./src/helpers/*"],"tests/*": ["./src/tests/*"]}}}
In this case, you can tell the TypeScript file resolver to support a number of custom prefixes to find code.
Note that this feature does not change how import paths are emitted bytsc
, sopaths
should only be used to inform TypeScript that another tool has this mapping and will use it at runtime or when bundling.
- Released:
# Resolve JSON Module -resolveJsonModule
Allows importing modules with a.json
extension, which is a common practice in node projects. This includesgenerating a type for theimport
based on the static JSON shape.
TypeScript does not support resolving JSON files by default:
tsTry
// @filename: settings.json{"repo":"TypeScript","dry":false,"debug":false}// @filename: index.tsimportCannot find module './settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.2732Cannot find module './settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.settings from"./settings.json" ;settings .debug ===true;settings .dry ===2;
Enabling the option allows importing JSON, and validating the types in that JSON file.
tsTry
// @filename: settings.json{"repo":"TypeScript","dry":false,"debug":false}// @filename: index.tsimportsettings from"./settings.json";settings .debug ===true;This comparison appears to be unintentional because the types 'boolean' and 'number' have no overlap.2367This comparison appears to be unintentional because the types 'boolean' and 'number' have no overlap.settings .dry ===2;
- Released:
# Resolve package.json Exports -resolvePackageJsonExports
--resolvePackageJsonExports
forces TypeScript to consulttheexports
field ofpackage.json
files if it ever reads from a package innode_modules
.
This option defaults totrue
under thenode16
,nodenext
, andbundler
options for--moduleResolution
.
- Default:
true
whenmoduleResolution
isnode16
,nodenext
, orbundler
; otherwisefalse
- Related:
- Released:
# Resolve package.json Imports -resolvePackageJsonImports
--resolvePackageJsonImports
forces TypeScript to consulttheimports
field ofpackage.json
files when performing a lookup that starts with#
from a file whose ancestor directory contains apackage.json
.
This option defaults totrue
under thenode16
,nodenext
, andbundler
options for--moduleResolution
.
- Default:
true
whenmoduleResolution
isnode16
,nodenext
, orbundler
; otherwisefalse
- Related:
- Released:
# rewriteRelativeImportExtensions -rewriteRelativeImportExtensions
Rewrite.ts
,.tsx
,.mts
, and.cts
file extensions in relative import paths to their JavaScript equivalent in output files.
For more information, see theTypeScript 5.7 release notes.
- Released:
# Root Dir -rootDir
Default: The longest common path of all non-declaration input files. Ifcomposite
is set, the default is instead the directory containing thetsconfig.json
file.
When TypeScript compiles files, it keeps the same directory structure in the output directory as exists in the input directory.
For example, let’s say you have some input files:
MyProj├── tsconfig.json├── core│ ├── a.ts│ ├── b.ts│ ├── sub│ │ ├── c.ts├── types.d.ts
The inferred value forrootDir
is the longest common path of all non-declaration input files, which in this case iscore/
.
If youroutDir
wasdist
, TypeScript would write this tree:
MyProj├── dist│ ├── a.js│ ├── b.js│ ├── sub│ │ ├── c.js
However, you may have intended forcore
to be part of the output directory structure.By settingrootDir: "."
intsconfig.json
, TypeScript would write this tree:
MyProj├── dist│ ├── core│ │ ├── a.js│ │ ├── b.js│ │ ├── sub│ │ │ ├── c.js
Importantly,rootDir
does not affect which files become part of the compilation.It has no interaction with theinclude
,exclude
, orfiles
tsconfig.json
settings.
Note that TypeScript will never write an output file to a directory outside ofoutDir
, and will never skip emitting a file.For this reason,rootDir
also enforces that all files which need to be emitted are underneath therootDir
path.
For example, let’s say you had this tree:
MyProj├── tsconfig.json├── core│ ├── a.ts│ ├── b.ts├── helpers.ts
It would be an error to specifyrootDir
ascore
andinclude
as*
because it creates a file (helpers.ts
) that would need to be emittedoutside theoutDir
(i.e.../helpers.js
).
- Default:
Computed from the list of input files.
- Released:
# Root Dirs -rootDirs
UsingrootDirs
, you can inform the compiler that there are many “virtual” directories acting as a single root.This allows the compiler to resolve relative module imports within these “virtual” directories, as if they were merged in to one directory.
For example:
src└── views└── view1.ts (can import "./template1", "./view2`)└── view2.ts (can import "./template1", "./view1`)generated└── templates└── views└── template1.ts (can import "./view1", "./view2")
{" ": {" ": ["src/views","generated/templates/views"]}}
This does not affect how TypeScript emits JavaScript, it only emulates the assumption that they will be able towork via those relative paths at runtime.
rootDirs
can be used to provide a separate “type layer” to files that are not TypeScript or JavaScript by providing a home for generated.d.ts
files in another folder. This technique is useful for bundled applications where you useimport
of files that aren’t necessarily code:
sh
src└── index.ts└── css└── main.css└── navigation.cssgenerated└── css└── main.css.d.ts└── navigation.css.d.ts
{" ": {" ": ["src","generated"]}}
This technique lets you generate types ahead of time for the non-code source files. Imports then work naturally based off the source file’s location.For example./src/index.ts
can import the file./src/css/main.css
and TypeScript will be aware of the bundler’s behavior for that filetype via the corresponding generated declaration file.
tsTry
// @filename: index.tsimport {appClass }from"./main.css";
- Default:
Computed from the list of input files.
- Released:
# Type Roots -typeRoots
By default allvisible ”@types
” packages are included in your compilation.Packages innode_modules/@types
of any enclosing folder are consideredvisible.For example, that means packages within./node_modules/@types/
,../node_modules/@types/
,../../node_modules/@types/
, and so on.
IftypeRoots
is specified,only packages undertypeRoots
will be included. For example:
{" ": {" ": ["./typings","./vendor/types"]}}
This config file will includeall packages under./typings
and./vendor/types
, and no packages from./node_modules/@types
.All paths are relative to thetsconfig.json
.
# Types -types
By default allvisible ”@types
” packages are included in your compilation.Packages innode_modules/@types
of any enclosing folder are consideredvisible.For example, that means packages within./node_modules/@types/
,../node_modules/@types/
,../../node_modules/@types/
, and so on.
Iftypes
is specified, only packages listed will be included in the global scope. For instance:
{" ": {" ": ["node","jest","express"]}}
Thistsconfig.json
file willonly include./node_modules/@types/node
,./node_modules/@types/jest
and./node_modules/@types/express
.Other packages undernode_modules/@types/*
will not be included.
What does this affect?
This option does not affect how@types/*
are included in your application code, for example if you had the abovecompilerOptions
example with code like:
ts
import*asmomentfrom"moment";moment().format("MMMM Do YYYY, h:mm:ss a");
Themoment
import would be fully typed.
When you have this option set, by not including a module in thetypes
array it:
- Will not add globals to your project (e.g
process
in node, orexpect
in Jest) - Will not have exports appear as auto-import recommendations
This feature differs fromtypeRoots
in that it is about specifying only the exact types you want included, whereastypeRoots
supports saying you want particular folders.
#Emit
# Declaration -declaration
Generate.d.ts
files for every TypeScript or JavaScript file inside your project.These.d.ts
files are type definition files which describe the external API of your module.With.d.ts
files, tools like TypeScript can provide intellisense and accurate types for un-typed code.
Whendeclaration
is set totrue
, running the compiler with this TypeScript code:
tsTry
exportlethelloWorld ="hi";
Will generate anindex.js
file like this:
tsTry
exportlethelloWorld ="hi";
With a correspondinghelloWorld.d.ts
:
tsTry
exportdeclarelethelloWorld:string;
When working with.d.ts
files for JavaScript files you may want to useemitDeclarationOnly
or useoutDir
to ensure that the JavaScript files are not overwritten.
- Default:
true
ifcomposite
;false
otherwise. - Related:
- Released:
# Declaration Dir -declarationDir
Offers a way to configure the root directory for where declaration files are emitted.
example├── index.ts├── package.json└── tsconfig.json
with thistsconfig.json
:
{" ": {" ":true," ":"./types"}}
Would place the d.ts for theindex.ts
in atypes
folder:
example├── index.js├── index.ts├── package.json├── tsconfig.json└── types└── index.d.ts
- Related:
- Released:
# Declaration Map -declarationMap
Generates a source map for.d.ts
files which map back to the original.ts
source file.This will allow editors such as VS Code to go to the original.ts
file when using features likeGo to Definition.
You should strongly consider turning this on if you’re using project references.
- Released:
# Downlevel Iteration -downlevelIteration
Downleveling is TypeScript’s term for transpiling to an older version of JavaScript.This flag is to enable support for a more accurate implementation of how modern JavaScript iterates through new concepts in older JavaScript runtimes.
ECMAScript 6 added several new iteration primitives: thefor / of
loop (for (el of arr)
), Array spread ([a, ...b]
), argument spread (fn(...args)
), andSymbol.iterator
.downlevelIteration
allows for these iteration primitives to be used more accurately in ES5 environments if aSymbol.iterator
implementation is present.
Example: Effects onfor / of
With this TypeScript code:
tsTry
conststr ="Hello!";for (consts ofstr ) {console .log (s );}
WithoutdownlevelIteration
enabled, afor / of
loop on any object is downleveled to a traditionalfor
loop:
tsTry
"use strict";varstr ="Hello!";for (var_i =0,str_1 =str;_i <str_1.length;_i++) {vars =str_1[_i];console.log(s);}
This is often what people expect, but it’s not 100% compliant with ECMAScript iteration protocol.Certain strings, such as emoji (😜), have a.length
of 2 (or even more!), but should iterate as 1 unit in afor-of
loop.Seethis blog post by Jonathan New for a longer explanation.
WhendownlevelIteration
is enabled, TypeScript will use a helper function that checks for aSymbol.iterator
implementation (either native or polyfill).If this implementation is missing, you’ll fall back to index-based iteration.
tsTry
"use strict";var__values = (this &&this.__values) ||function(o) {vars =typeofSymbol ==="function" &&Symbol.iterator,m =s &&o[s],i =0;if (m)returnm.call(o);if (o &&typeofo.length ==="number")return {next:function () {if (o &&i >=o.length)o =void0;return {value:o &&o[i++],done: !o };}};thrownewTypeError(s ?"Object is not iterable." :"Symbol.iterator is not defined.");};vare_1,_a;varstr ="Hello!";try {for (varstr_1 =__values(str),str_1_1 =str_1.next(); !str_1_1.done;str_1_1 =str_1.next()) {vars =str_1_1.value;console.log(s);}}catch (e_1_1) {e_1 = {error:e_1_1 }; }finally {try {if (str_1_1 && !str_1_1.done && (_a =str_1.return))_a.call(str_1);}finally {if (e_1)throwe_1.error; }}
You can usetslib viaimportHelpers
to reduce the amount of inline JavaScript too:
tsTry
"use strict";var__values = (this &&this.__values) ||function(o) {vars =typeofSymbol ==="function" &&Symbol.iterator,m =s &&o[s],i =0;if (m)returnm.call(o);if (o &&typeofo.length ==="number")return {next:function () {if (o &&i >=o.length)o =void0;return {value:o &&o[i++],done: !o };}};thrownewTypeError(s ?"Object is not iterable." :"Symbol.iterator is not defined.");};vare_1,_a;varstr ="Hello!";try {for (varstr_1 =__values(str),str_1_1 =str_1.next(); !str_1_1.done;str_1_1 =str_1.next()) {vars =str_1_1.value;console.log(s);}}catch (e_1_1) {e_1 = {error:e_1_1 }; }finally {try {if (str_1_1 && !str_1_1.done && (_a =str_1.return))_a.call(str_1);}finally {if (e_1)throwe_1.error; }}
Note: enablingdownlevelIteration
does not improve compliance ifSymbol.iterator
is not present in the runtime.
Example: Effects on Array Spreads
This is an array spread:
js
// Make a new array whose elements are 1 followed by the elements of arr2constarr = [1, ...arr2];
Based on the description, it sounds easy to downlevel to ES5:
js
// The same, right?constarr = [1].concat(arr2);
However, this is observably different in certain rare cases.
For example, if a source array is missing one or more items (contains a hole), the spread syntax will replace each empty item withundefined
, whereas.concat
will leave them intact.
js
// Make an array where the element at index 1 is missingletarrayWithHole = ["a", ,"c"];letspread = [...arrayWithHole];letconcatenated = [].concat(arrayWithHole);console.log(arrayWithHole);// [ 'a', <1 empty item>, 'c' ]console.log(spread);// [ 'a', undefined, 'c' ]console.log(concatenated);// [ 'a', <1 empty item>, 'c' ]
Just as withfor / of
,downlevelIteration
will useSymbol.iterator
(if present) to more accurately emulate ES 6 behavior.
- Related:
- Released:
# Emit BOM -emitBOM
Controls whether TypeScript will emit abyte order mark (BOM) when writing output files.Some runtime environments require a BOM to correctly interpret a JavaScript files; others require that it is not present.The default value offalse
is generally best unless you have a reason to change it.
- Released:
# Emit Declaration Only -emitDeclarationOnly
Only emit.d.ts
files; do not emit.js
files.
This setting is useful in two cases:
- You are using a transpiler other than TypeScript to generate your JavaScript.
- You are using TypeScript to only generate
d.ts
files for your consumers.
- Related:
- Released:
# Import Helpers -importHelpers
For certain downleveling operations, TypeScript uses some helper code for operations like extending class, spreading arrays or objects, and async operations.By default, these helpers are inserted into files which use them.This can result in code duplication if the same helper is used in many different modules.
If theimportHelpers
flag is on, these helper functions are instead imported from thetslib module.You will need to ensure that thetslib
module is able to be imported at runtime.This only affects modules; global script files will not attempt to import modules.
For example, with this TypeScript:
ts
exportfunctionfn(arr:number[]) {constarr2 = [1, ...arr];}
Turning ondownlevelIteration
andimportHelpers
is still false:
tsTry
var__read = (this &&this.__read) ||function (o,n) {varm =typeofSymbol ==="function" &&o[Symbol.iterator];if (!m)returno;vari =m.call(o),r,ar = [],e;try {while ((n ===void0 ||n-- >0) && !(r =i.next()).done)ar.push(r.value);}catch (error) {e = {error:error }; }finally {try {if (r && !r.done && (m =i["return"]))m.call(i);}finally {if (e)throwe.error; }}returnar;};var__spreadArray = (this &&this.__spreadArray) ||function (to,from,pack) {if (pack ||arguments.length ===2)for (vari =0,l =from.length,ar;i <l;i++) {if (ar || !(iinfrom)) {if (!ar)ar =Array.prototype.slice.call(from,0,i);ar[i] =from[i];}}returnto.concat(ar ||Array.prototype.slice.call(from));};exportfunctionfn(arr) {vararr2 =__spreadArray([1],__read(arr),false);}
Then turning on bothdownlevelIteration
andimportHelpers
:
tsTry
import {__read,__spreadArray }from"tslib";exportfunctionfn(arr) {vararr2 =__spreadArray([1],__read(arr),false);}
You can usenoEmitHelpers
when you provide your own implementations of these functions.
- Related:
- Released:
# Inline Source Map -inlineSourceMap
When set, instead of writing out a.js.map
file to provide source maps, TypeScript will embed the source map content in the.js
files.Although this results in larger JS files, it can be convenient in some scenarios.For example, you might want to debug JS files on a webserver that doesn’t allow.map
files to be served.
Mutually exclusive withsourceMap
.
For example, with this TypeScript:
ts
consthelloWorld ="hi";console.log(helloWorld);
Converts to this #"use strict";
Then enable building it withinlineSourceMap
enabled there is a comment at the bottom of the file which includesa source-map for the file.
tsTry
"use strict";consthelloWorld ="hi";console.log(helloWorld);//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDO0FBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMifQ==
- Released:
# Inline Sources -inlineSources
When set, TypeScript will include the original content of the.ts
file as an embedded string in the source map (using the source map’ssourcesContent
property).This is often useful in the same cases asinlineSourceMap
.
Requires eithersourceMap
orinlineSourceMap
to be set.
For example, with this TypeScript:
tsTry
consthelloWorld ="hi";console .log (helloWorld );
By default converts to this #"use strict";
Then enable building it withinlineSources
andinlineSourceMap
enabled there is a comment at the bottom of the file which includesa source-map for the file.Note that the end is different from the example ininlineSourceMap
because the source-map now contains the original source code also.
tsTry
"use strict";consthelloWorld ="hi";console.log(helloWorld);//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDO0FBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBoZWxsb1dvcmxkID0gXCJoaVwiO1xuY29uc29sZS5sb2coaGVsbG9Xb3JsZCk7Il19
- Released: