126

I'm trying to convert a pet project to TypeScript and don't seem to be able to use the tsc utility to watch and compile my files. The help says I should use the -w switch, but it looks like it can't watch and compile all *.ts files in the some directory recursively. This seems like something tsc should be able to handle. What are my options?

VoY
  • 5,479
  • 2
  • 37
  • 45
  • 2
    Warning: this question and many of answers are dated. Here is a shameless plug for my up-to-date answer: https://stackoverflow.com/a/66104668/109618 – David J. Feb 08 '21 at 15:41

12 Answers12

172

Create a file named tsconfig.json in your project root and include following lines in it:

{
    "compilerOptions": {
        "emitDecoratorMetadata": true,
        "module": "commonjs",
        "target": "ES5",
        "outDir": "ts-built",
        "rootDir": "src"
    }
}

Please note that outDir should be the path of the directory to receive compiled JS files, and rootDir should be the path of the directory containing your source (.ts) files.

Open a terminal and run tsc -w, it'll compile any .ts file in src directory into .js and store them in ts-built directory.

budhajeewa
  • 2,268
  • 1
  • 17
  • 19
  • 1
    Thanks for that solution. One small update: `tsc` gave an error, `error TS6050: Unable to open file 'tsconfig.json'.` until I removed the comments – Garrett McCullough Oct 06 '15 at 18:27
  • @gwmccull: Ah, I only added them in here, so that readers would know what's what. I'll update the answer. – budhajeewa Oct 07 '15 at 00:56
  • The comments were helpful. It just took me a minute to figure out why it wouldn't work. The new note works too. Thanks for the answer! – Garrett McCullough Oct 07 '15 at 04:00
  • 8
    Just in case someone is looking for it. Note from this link: https://github.com/Microsoft/TypeScript/wiki/tsconfig.json "If no "files" property is present in a tsconfig.json, the compiler defaults to including all TypeScript (*.ts or *.tsx) files in the containing directory and subdirectories. When a "files" property is present, only the specified files are included." – carey walker Feb 25 '16 at 23:24
  • @GarrettMcCullough .json unfortunately doesn't support comments :( – Deian Oct 25 '16 at 03:05
  • 3
    instead of setting a single source directory with `compilerOptions.rootDir`, you can specify multiple source directories with tsconfig's `include` property: `{ "compilerOptions": { ...myOptions }, "include": [ "src", "otherSrc" ] }` – JP Lew May 04 '18 at 17:32
  • Downvoted because `rootDir` is not properly explained; the example above, in my opinion, is also misleading. From [the TS docs](https://www.typescriptlang.org/tsconfig#rootDir) "Importantly, `rootDir` does not affect which files become part of the compilation. It has no interaction with the include, exclude, or files tsconfig.json settings." – David J. Feb 08 '21 at 15:26
  • This answer includes several irrelevant tsconfig options, such as `emitDecoratorMetadata` and `module`; it would be better to only include applicable options pertaining to the question. – David J. Feb 08 '21 at 15:27
33

TypeScript 1.5 beta has introduced support for a configuration file called tsconfig.json. In that file you can configure the compiler, define code formatting rules and more importantly for you, provide it with information about the TS files in your project.

Once correctly configured, you can simply run the tsc command and have it compile all the TypeScript code in your project.

If you want to have it watch the files for changes then you can simply add --watch to the tsc command.

Here's an example tsconfig.json file

{
"compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "declaration": false,
    "noImplicitAny": false,
    "removeComments": true,
    "noLib": false
},
"include": [
    "**/*"
],
"exclude": [
    "node_modules",
    "**/*.spec.ts"
]}

In the example above, I include all .ts files in my project (recursively). Note that you can also exclude files using an "exclude" property with an array.

For more information, refer to the documentation: http://www.typescriptlang.org/docs/handbook/tsconfig-json.html

BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
dSebastien
  • 1,983
  • 2
  • 21
  • 31
  • 2
    Is glob syntax really implemented? It is not in the schema – Serguzest Aug 20 '15 at 00:23
  • 2
    Actually no; I've discovered since then that glob patterns are still under discussion. filesGlob is only supported by the Atom editor. For now you can specify the 'files' and 'exclude' properties. – dSebastien Sep 05 '15 at 09:27
  • 2
    The issue to keep an eye on is the following: https://github.com/Microsoft/TypeScript/pull/3232 – dSebastien Sep 05 '15 at 09:27
30

you can watch all files like this

tsc *.ts --watch
Officialzessu
  • 897
  • 7
  • 6
  • 12
    "File '*.ts' not found" is what I get if I apply this solution in node. Any thoughts on this please? – Sami Aug 05 '19 at 08:57
  • 2
    2 years late, but I'm adding this for anyone who found this off of search engines: you can usually remove the "*.ts" part of the command. Might solve that issue ^^^ – DexieTheSheep Nov 29 '21 at 08:36
28

The other answers may have been useful years ago, but they are now out of date.

Given that a project has a tsconfig file, run this command...

tsc --watch

... to watch for changed files and compile as needed. The documentation explains:

Run the compiler in watch mode. Watch input files and trigger recompilation on changes. The implementation of watching files and directories can be configured using environment variable. See configuring watch for more details.

To answer the original question, recursive directory watching is possible even on platforms that don't have native support, as explained by the Configuring Watch docs:

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

David J.
  • 31,569
  • 22
  • 122
  • 174
8

Technically speaking you have a few options here:

If you are using an IDE like Sublime Text and integrated MSN plugin for Typescript: http://blogs.msdn.com/b/interoperability/archive/2012/10/01/sublime-text-vi-emacs-typescript-enabled.aspx you can create a build system which compile the .ts source to .js automatically. Here is the explanation how you can do it: How to configure a Sublime Build System for TypeScript.

You can define even to compile the source code to destination .js file on file save. There is a sublime package hosted on github: https://github.com/alexnj/SublimeOnSaveBuild which make this happen, only you need to include the ts extension in the SublimeOnSaveBuild.sublime-settings file.

Another possibility would be to compile each file in the command line. You can compile even multiple files at once by separating them with spaces like so: tsc foo.ts bar.ts. Check this thread: How can I pass multiple source files to the TypeScript compiler?, but i think the first option is more handy.

Community
  • 1
  • 1
Endre Simo
  • 11,330
  • 2
  • 40
  • 49
7

The tsc compiler will only watch those files that you pass on the command line. It will not watch files that are included using a /// <sourcefile> reference. If your working with the bash, you could use find to recursively find all *.ts files and compile them:

find . -name "*.ts" | xargs tsc -w
Valentin
  • 7,874
  • 5
  • 33
  • 38
6

Look into using grunt to automate this, there are numerous tutorials around, but here's a quick start.

For a folder structure like:

blah/
blah/one.ts
blah/two.ts
blah/example/
blah/example/example.ts
blah/example/package.json
blah/example/Gruntfile.js
blah/example/index.html

You can watch and work with typescript easily from the example folder with:

npm install
grunt

With package.json:

{
  "name": "PROJECT",
  "version": "0.0.1",
  "author": "",
  "description": "",
  "homepage": "",
  "private": true,
  "devDependencies": {
    "typescript": "~0.9.5",
    "connect": "~2.12.0",
    "grunt-ts": "~1.6.4",
    "grunt-contrib-watch": "~0.5.3",
    "grunt-contrib-connect": "~0.6.0",
    "grunt-open": "~0.2.3"
  }
}

And a grunt file:

module.exports = function (grunt) {

  // Import dependencies
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-open');
  grunt.loadNpmTasks('grunt-ts');

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    connect: {
      server: {  // <--- Run a local server on :8089
        options: {
          port: 8089,
          base: './'
        }
      }
    },
    ts: {
      lib: { // <-- compile all the files in ../ to PROJECT.js
        src: ['../*.ts'],
        out: 'PROJECT.js',
        options: {
          target: 'es3',
          sourceMaps: false,
          declaration: true,
          removeComments: false
        }
      },
      example: {  // <--- compile all the files in . to example.js
        src: ['*.ts'],
        out: 'example.js',
        options: {
          target: 'es3',
          sourceMaps: false,
          declaration: false,
          removeComments: false
        }
      }
    },
    watch: { 
      lib: { // <-- Watch for changes on the library and rebuild both
        files: '../*.ts',
        tasks: ['ts:lib', 'ts:example']
      },
      example: { // <--- Watch for change on example and rebuild
        files: ['*.ts', '!*.d.ts'],
        tasks: ['ts:example']
      }
    },
    open: { // <--- Launch index.html in browser when you run grunt
      dev: {
        path: 'http://localhost:8089/index.html'
      }
    }
  });

  // Register the default tasks to run when you run grunt
  grunt.registerTask('default', ['ts', 'connect', 'open', 'watch']);
}
Doug
  • 32,844
  • 38
  • 166
  • 222
3

tsc 0.9.1.1 does not seem to have a watch feature.

You could use a PowerShell script like the one:

#watch a directory, for changes to TypeScript files.  
#  
#when a file changes, then re-compile it.  
$watcher = New-Object System.IO.FileSystemWatcher  
$watcher.Path = "V:\src\MyProject"  
$watcher.IncludeSubdirectories = $true  
$watcher.EnableRaisingEvents = $true  
$changed = Register-ObjectEvent $watcher "Changed" -Action {  
  if ($($eventArgs.FullPath).EndsWith(".ts"))  
  {  
    $command = '"c:\Program Files (x86)\Microsoft SDKs\TypeScript\tsc.exe" "$($eventArgs.FullPath)"'  
    write-host '>>> Recompiling file ' $($eventArgs.FullPath)  
    iex "& $command"  
  }  
}  
write-host 'changed.Id:' $changed.Id  
#to stop the watcher, then close the PowerShell window, OR run this command:  
# Unregister-Event < change Id >  

Ref: Automatically watch and compile TypeScript files.

Blue
  • 22,608
  • 7
  • 62
  • 92
Sean
  • 1,025
  • 9
  • 17
1

Today I designed this Ant MacroDef for the same problem as yours :

    <!--
    Recursively read a source directory for TypeScript files, generate a compile list in the
    format needed by the TypeScript compiler adding every parameters it take.
-->
<macrodef name="TypeScriptCompileDir">

    <!-- required attribute -->
    <attribute name="src" />

    <!-- optional attributes -->
    <attribute name="out" default="" />
    <attribute name="module" default="" />
    <attribute name="comments" default="" />
    <attribute name="declarations" default="" />
    <attribute name="nolib" default="" />
    <attribute name="target" default="" />

    <sequential>

        <!-- local properties -->
        <local name="out.arg"/>
        <local name="module.arg"/>
        <local name="comments.arg"/>
        <local name="declarations.arg"/>
        <local name="nolib.arg"/>
        <local name="target.arg"/>
        <local name="typescript.file.list"/>
        <local name="tsc.compile.file"/>

        <property name="tsc.compile.file" value="@{src}compile.list" />

        <!-- Optional arguments are not written to compile file when attributes not set -->
        <condition property="out.arg" value="" else='--out "@{out}"'>
            <equals arg1="@{out}" arg2="" />
        </condition>

        <condition property="module.arg" value="" else="--module @{module}">
            <equals arg1="@{module}" arg2="" />
        </condition>

        <condition property="comments.arg" value="" else="--comments">
            <equals arg1="@{comments}" arg2="" />
        </condition>

        <condition property="declarations.arg" value="" else="--declarations">
            <equals arg1="@{declarations}" arg2="" />
        </condition>

        <condition property="nolib.arg" value="" else="--nolib">
            <equals arg1="@{nolib}" arg2="" />
        </condition>

        <!-- Could have been defaulted to ES3 but let the compiler uses its own default is quite better -->
        <condition property="target.arg" value="" else="--target @{target}">
            <equals arg1="@{target}" arg2="" />
        </condition>

        <!-- Recursively read TypeScript source directory and generate a compile list -->
        <pathconvert property="typescript.file.list" dirsep="\" pathsep="${line.separator}">

            <fileset dir="@{src}">
                <include name="**/*.ts" />
            </fileset>

            <!-- In case regexp doesn't work on your computer, comment <mapper /> and uncomment <regexpmapper /> -->
            <mapper type="regexp" from="^(.*)$" to='"\1"' />
            <!--regexpmapper from="^(.*)$" to='"\1"' /-->

        </pathconvert>


        <!-- Write to the file -->
        <echo message="Writing tsc command line arguments to : ${tsc.compile.file}" />
        <echo file="${tsc.compile.file}" message="${typescript.file.list}${line.separator}${out.arg}${line.separator}${module.arg}${line.separator}${comments.arg}${line.separator}${declarations.arg}${line.separator}${nolib.arg}${line.separator}${target.arg}" append="false" />

        <!-- Compile using the generated compile file -->
        <echo message="Calling ${typescript.compiler.path} with ${tsc.compile.file}" />
        <exec dir="@{src}" executable="${typescript.compiler.path}">
            <arg value="@${tsc.compile.file}"/>
        </exec>

        <!-- Finally delete the compile file -->
        <echo message="${tsc.compile.file} deleted" />
        <delete file="${tsc.compile.file}" />

    </sequential>

</macrodef>

Use it in your build file with :

    <!-- Compile a single JavaScript file in the bin dir for release -->
    <TypeScriptCompileDir
        src="${src-js.dir}"
        out="${release-file-path}"
        module="amd"
    />

It is used in the project PureMVC for TypeScript I'm working on at the time using Webstorm.

Tekool
  • 27
  • 4
  • Ant microscript? You might want to expand the answer to explain how to use that as part of this solution.... – random_user_name Oct 24 '12 at 23:02
  • I'll try to make a blog post giving a simple example and link this here. If you can't wait here is the project where I'm using it https://github.com/tekool/puremvc-typescript-singlecore for which the complete Ant build file is : https://github.com/tekool/puremvc-typescript-singlecore/blob/master/build/build.xml – Tekool Oct 25 '12 at 21:14
0

EDIT: Note, this is if you have multiple tsconfig.json files in your typescript source. For my project we have each tsconfig.json file compile to a differently-named .js file. This makes watching every typescript file really easy.

I wrote a sweet bash script that finds all of your tsconfig.json files and runs them in the background, and then if you CTRL+C the terminal it will close all the running typescript watch commands.

This is tested on MacOS, but should work anywhere that BASH 3.2.57 is supported. Future versions may have changed some things, so be careful!

#!/bin/bash
# run "chmod +x typescript-search-and-compile.sh" in the directory of this file to ENABLE execution of this script
# then in terminal run "path/to/this/file/typescript-search-and-compile.sh" to execute this script
# (or "./typescript-search-and-compile.sh" if your terminal is in the folder the script is in)

# !!! CHANGE ME !!!    
# location of your scripts root folder
# make sure that you do not add a trailing "/" at the end!!
# also, no spaces! If you have a space in the filepath, then
# you have to follow this link: https://stackoverflow.com/a/16703720/9800782
sr=~/path/to/scripts/root/folder
# !!! CHANGE ME !!!

# find all typescript config files
scripts=$(find $sr -name "tsconfig.json")

for s in $scripts
do
    # strip off the word "tsconfig.json"
    cd ${s%/*} # */ # this function gets incorrectly parsed by style linters on web
    # run the typescript watch in the background
    tsc -w &
    # get the pid of the last executed background function
    pids+=$!
    # save it to an array
    pids+=" "
done

# end all processes we spawned when you close this process
wait $pids

Helpful resources:

Matt Wyndham
  • 371
  • 3
  • 6
0

In linux I use:

tsc -w $(find . | grep .ts)

This will watch every typescript file under the current directory.

Dharman
  • 30,962
  • 25
  • 85
  • 135
pablozoani
  • 33
  • 5
0

when you run your code with ts-node-dev it will solve most of the issues

  1. ts-node-dev src/index.ts will compile only its dependencies JIT
  2. on change recompile and restart server
Michal Miky Jankovský
  • 3,089
  • 1
  • 35
  • 36