Repository navigation
Skip typechecking; only emit (support --transpileOnly in tsc, re-open of #4176) #29651
Description
Search Terms
isolatedModules, incremental build slow, allowJs, transpileOnly. #4176
Suggestion
Support a compilation mode where files are only transpiled without typechecking. This can greatly improve compilation speed. Similar to the transpileOnly flag in ts-node and ts-loader.
Use Cases
At Taskworld (@taskworld), we are trying to migrate our project to TypeScript. We have 1400 source files.
As we try to get our .js files to be processed and transpiled by tsc, setting "allowJs": true makes tsc take a painfully long time (40 seconds) to complete, even in --watch mode. tsc --diagnostics shows that lots of time is spent in typechecking phase.
I have checked these issues:
-
typescript --allowJs too slow #7808
No resolution. -
2x compilation slowdown when upgrading from TS1.8 => TS2.0 #10018
Irrelevant — compiler speed has since been improved but that did not solve our problem. -
TypeScript Extremely Slow #21221
I usedtsc --listFilesto check the files included in the compilation unit. Looks normal. In that issue, they solved the problem by renaming all.jsto.ts, which we can’t do yet without causing errors all over the place, due to our JS files are still CommonJS modules, which is only recognized when using JS files. I try to migrate with minimal code changes, so I avoid mass-converting CommonJS to ES modules for now. -
Strategies for improving incremental Check time? #13538
People at Google solved this problem by separating their project into libraries and creates their own build system (based on Bazel). Since I tried to add the tooling with minimal code change, we don’t want to “split our code base into multiple units” right now. -
Very slow compilation with allowJs #10157
OP solved this problem by usingtranspileOnlymode ints-loader, which uses the “single-module transpilation mode” (thets.transpileModuleAPI). However, we aren’t usingwebpackand we’re directly usingtscto compile.tsfiles to.jsfiles. -
TypeScript build is slow, compileOnSave/--watch taking ~1minute #22953
No resolution.
I tried to profile the tsc process, and found that a lot of time is spent in resolveCallSignature.
If we can skip the type-checking process, this compilation phase should be faster.
This seems to be supported in both ts-node and ts-loader, since TypeScript provides the “single-module transpilation mode” (the ts.transpileModule API). So, I looked for a way to do it using tsc. Turns out, it is not available, and we have to somehow use the ts.transpileModule API directly.
A fancier solution would be to use the compiler's
transpileAPI directly.
If you are willing to get your entire project compiling under the
isolatedModulesswitch, then you can safely wire up your build system to do a simple emit of only changed files, which should be practically instant, followed by a re-typecheck.
Examples
All evidence so far suggests that we have to build our own tooling which behaves like babel -d build-dir source-dir (e.g. compiles each file separately) but for TypeScript. And so we implemented our own workaround:
// tsc-fast.js
const args = require('yargs')
.options({
force: {
alias: 'f',
description: 'Recompiles even if output file is newer.',
type: 'boolean',
},
watch: {
alias: 'w',
description: 'Watches for file changes.',
type: 'boolean',
},
})
.strict()
.help()
.parse()
const watch = require('gulp-watch')
const ts = require('gulp-typescript')
const newer = require('gulp-newer')
const tsProject = ts.createProject('tsconfig.json', {
isolatedModules: true,
})
const vfs = require('vinyl-fs')
const debug = require('gulp-debug')
const sourcemaps = require('gulp-sourcemaps')
function main() {
let compiling = false
let pending = false
function compile() {
if (compiling) {
pending = true
return
}
compiling = true
const rawInput = tsProject.src()
const input = args.force
? rawInput
: rawInput.pipe(
newer({
dest: 'dist',
map: f => f.replace(/\.ts$/, '.js'),
})
)
input
.pipe(sourcemaps.init())
.pipe(tsProject())
.pipe(sourcemaps.write('.'))
.on('error', () => {
/* Ignore compiler errors */
})
.pipe(debug({ title: 'tsc:' }))
.pipe(vfs.dest('dist'))
.on('end', () => {
compiling = false
if (pending) {
pending = false
compile()
}
})
}
compile()
if (args.watch) {
watch(['app/**/*.js', 'app/**/*.ts', '!app/vpc-admin/front/**/*'], compile)
}
}
main()To typecheck in separate step, we simply run tsc --noEmit in a separate CI job. Also, VS Code takes care of typechecking in the editor, so we already get instant feedback for type errors.
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Activity
[-]Skip typechecking; only emit (--transpileOnly, re-open of #4176)[/-][+]Skip typechecking; only emit (support `--transpileOnly` in `tsc`, re-open of #4176)[/+]dtinth commentedon Feb 27, 2019
I noticed the questions in #30117 and would like to give some answers for context:
If this has to do with
.on('error', () => { /* Ignore compiler errors */ }), this only prevents the script from crashing Node.js when a compiler error occurs.gulp-typescriptoutputs error messages to console already.I realize that I haven’t put the diagnostic information in the issue.
This is the diagnostic info for incremental compilation.
Here’s the corresponding CPU profile:
As you can see, about 80% of “Check time” is spent in
checkCallExpressionfunction. So maybe there might be some slow stuff going on there — I may have to do a more thorough investigation.This is a module project, but
allowJsis on and most of JS files are CommonJS modules.(cc: Ryan Cavanaugh (@RyanCavanaugh))
RyanCavanaugh commentedon Apr 1, 2019
Discussed. We felt like we had a good handle on this and then totally deadlocked on the question of whether this mode would imply
--noResolve:transpileOnlySeparately, the problem that this mode only "works" if
--isolatedModulesis on was a point against it.Ultimately a tool that just calls
ts.transpileModuleon some defined set of files is quite trivial to write, and would have clearer semantics than what we might provide out of the box.We were curious if things got better for you with
--incremental.biels commentedon May 7, 2019
I would add another reason c) treat all ts errors as warnings and still emit the results.
This is available on browser projects when using webpack with ts-loader using
transpileOnly: true.The advantage of this workflow is that it enables faster prototyping when combined with hot reload / run on save. You can see type errors alongside runtime errors and get the best of both worlds. You still care about ts errors as they show up in your IDE and build logs but they do not necessarily break your build.
This may not be desired on all projects or environments but that's where the flag comes in. I would suggest to add a
transpileOnlyboolean flag to the compliler so that it supports this behavior natively and can be used for projects that do not target the browser.ayroblu commentedon Jun 24, 2019
tsccan emit even if there are errors (how we quickly fix bugs by trying things)Looking at this cause
--incrementaldoesn't re emit files that haven't changed, nor does it remove files that have been deleted (which is why I want this)ts-nodealready has a fast flag, so it'd be nice to have this hereayroblu commentedon Jun 24, 2019
Discovered
typescript-transpile-onlyguess this solves my problems:https://github.com/cspotcode/typescript-transpile-only
biels commentedon Jun 24, 2019
canvural commentedon Sep 27, 2019
Another reason this would be great is compiling in the production environment. In production we don't install dev dependencies, so running
tscon production results in an error because it can't find type definitions. But our code is checked before it reaches to production, so in production, we know its fine type wise.sheerun commentedon Oct 3, 2019
My use case is quickly transiling typescript snippet so I can use it in non-typescript project. Currently I need to use online playground for this...
kitsonk commentedon Nov 13, 2019
There is feedback for Deno that having a transpile only option might be a viable way to speed up startup of some workloads (denoland/deno#3321) While we cache the output of a compile, if you are loading some known good TypeScript for the first time, just parsing and emitting, and not type checking, might be a good option.
weswigham commentedon Nov 13, 2019
Kitson Kelly (@kitsonk) if you have code you assume is "known good", why not add some more into that definition of "known good", rather than just "was type checked", and support shipping and loading a bytecode? Kinda like
.pycfiles for python.27 remaining items
hanseltime commentedon Dec 22, 2022
Just saw Julien Barbay (@y-nk) 's comment. I would like to note that the package I wrote takes advantage of the node_modules folder. I've been doing a lot more with yarn pnp recently and think there would need to be some additional lift for that. I don't currently have the time to add that configuration but anyone who would like to play with that need, can.
beenotung commentedon Jan 9, 2023
I published a cli package live-tsc that do transpiling without type checking.
It is a recursive wrapper built on top of esbuild, with support on watch mode and adding custom post-hook.
martinjlowm commentedon Jan 22, 2023
We use direct Git repo references for our packages and we ended up with the following ESBuild script for emitting CJS, ESM and declaration files upon package installation (postinstall) (inspired by Beeno Tung (@beenotung)'s script):
Obviously, it adds a bit of overhead to have
esbuild-*packages as production dependencies - but it definitely beats having to store dist files in source control. Hope somebody finds it useful :)thorsent commentedon Oct 4, 2023
Use case: trying to compile some random repo that has out of date typedefs. Not my code, and I just need to transpile and run the thing to get a result. I frankly had no idea up until this minute that transpileOnly was ts-node! I had assumed all along that it was a tsc feature.
bschlenk commentedon Jan 25, 2024
Maybe another use case. My company uses a monorepo with pnpm. All our pnpm workspaces are written in typescript, and we have to build them before they'll work when installed in other packages. We know all these packages were already typechecked in ci. What I want to do is build with esbuild, and generate .d.ts files with tsc. I'm wondering if skipping the type checking here would speed up the .d.ts file generation any significant amount.
acutmore commentedon Feb 11, 2024
Brian Schlenker (@bschlenk) it sounds like you would be interested in #47947, which enables
.d.tsgeneration without type checking.inoyakaigor commentedon Apr 25, 2024
One more use case
Running tests in built-in Nodejs test runner.
All my tests has written in TS and obviously Nodejs can't execute it without precompilation into JS. I can do this in this way:
My code is not perfect and contain a bunch of type errors and it is OKAY. For typeschecking I have another solution so in my tests I would test only my code not types. For that it would be great if there exists CLI command like
--transpileOnlyjakebailey commentedon Jul 18, 2024
TypeScript 5.5 added
--noCheck; is there any other work to be done?DanielRosenwasser commentedon Jul 18, 2024
I believe it's not user-facing in 5.5, even on the API level. #58839 makes it broadly accessible though, so we can mark this as fixed.
jakebailey commentedon Jul 18, 2024
Oops, typo, I meant 5.6!
Yegorich555 commentedon Oct 8, 2024
Well... --noCheck is console-argument. How to use it with

ts.createWatchCompilerHost(...)where I need to setts.CompilerOptions? Any thoughts?Expected that in options I could point noCheck into compiler options directly too...
fix(build): add flag to tsc to ignore warnings