Is there a way to do conditional compilation in TypeScript using #if, #endif directives for example? I wish I was able to enable/disable tracing.
6 Answers
There isn't currently, but vote for the feature request so that it makes it in someday!. The feature request at microsoft/TypeScript#3538 has been declined as out of scope for TypeScript.
Comments
You can use global_defenitions option of UglifyJs for doing this. For example if you pass:
global_defs: {
DEBUG: false
}
the compressor will assume that's a constant defintion and will discard code like this as being unreachable:
if (DEBUG) {
...
}
Comments
TL;DR: No, use MSBuild conditions as a manual workaround
There are no conditionals in typescript, however if you are like me, you would be fine with an ugly (but stable) workaround to achieve compile time conditionals.
In case you are developing typescript outside of visual studio, you can use something like the C preprocessor in a build step before the typescript compiler runs. You probably knew this.
If you are in visual studio, it's not that simple (could be done, but it's cumbersome). Instead you can use MSBuild conditional constructs in your project and a bit of javascript abuse to achieve a similar effect.
The following snippet assumes you have the configurations Debug and Release and the two files Config.Release.ts and Config.Debug.ts.
- to include it, unload your project (
right click->Unload project) - then open it as xml (
right click->Edit foo.csproj). - remove the conditional items (
Config.(Release|Debug).ts) from the unconditional item group - add the snippet below the unconditional item group
<Choose>
<When Condition=" '$(Configuration)'=='Debug' ">
<ItemGroup>
<TypeScriptCompile Include="Config.Debug.ts" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<None Include="Config.Debug.ts" />
</ItemGroup>
</Otherwise>
</Choose>
<Choose>
<When Condition=" '$(Configuration)'=='Release' ">
<ItemGroup>
<TypeScriptCompile Include="Config.Release.ts" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<None Include="Config.Release.ts" />
</ItemGroup>
</Otherwise>
</Choose>
This will compile one file or the other based on which configuration you are using, so you can conditionally 'patch in' the tracing behavior (or disable it).
// Config.Release.ts
/// <reference "Trace.ts" />
module Config {
import TraceService = Trace.TraceService;
TraceService.trace = function(msg: string) {}
}
// Config.Debug.ts
/// <reference "Trace.ts" />
module Config {
import TraceService = Trace.TraceService;
TraceService.trace = function(msg: string) { console.log(msg); }
}
This technique is cumbersome and requires manual patching of the proj file, but it's stable and it works OK until the TypeScript devs give in to popular demand or Visual Studio decides to add a precompiler which works well with MSBuild.
It's been over a year since I created this hack, but the situation has not changed. TypeScript is still reluctant to introduce conditional compilation and that is not likely to change.
Please note that this workaround as it is does not work for projects with a tsconfig.json.
If there is a tsconfig.json in the root of the project, all the <TypeScriptCompile/> directives are ignored and only the tsconfig.json defines what gets compiled and what doesn't.
Of course the solution to that problem is multiple tsconfig.json files and more MSBuild voodoo.
If you need that and can't figure out how to do it yourself, drop a comment.
4 Comments
You can't do compile time directives. The best option is to define a tracing method including all the calls to it and to turn it on define it for real and to turn it off define the method as a null op (function () {}). You'll still have the overhead of the method call at runtime, but it's method call to an empty method--little overhead.
Comments
There is not. Since comments are preserved during compilation, you can use a separate tool in your build process to emulate this.
1 Comment
tsc in package scripts.Angular, React, Vue and many other popular JavaScript/TypeScript frameworks now support a Vite build option. If you're using TypeScript with Vite, you can use Vite to conditionally include/exclude blocks of TypeScript code.
Even if you're not using Vite, a plugin discussed below supports conditional pre-processing in many other builders (including Vite) that can achieve the same result.
Use Vite import.meta.env with dead code elimination (DCE).
Vite exposes environment variables through import.meta.env. You can define custom environment variables in .env files (e.g., .env.development, .env.production) and access them in your code. Vite automatically replaces import.meta.env references during the build process, enabling dead code elimination if conditions are met.
- Reference: Vite - Env Variables and Modes
if (import.meta.env.VITE_FEATURE_ENABLED) {
// included only if VITE_FEATURE_ENABLED is true
console.log('Feature is enabled!');
} else {
// Code to be included otherwise
console.log('Feature is disabled.');
}
Use Vite defineConfig() to configure plugins / settings for development/production/staging (modes).
Vite's defineConfig() function that allows you to set up different build configurations based on development or production build mode or other environment variables. You can use this to conditionally add plugins, set up import aliases, or adjust other build options.
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig(({ mode }) =\> {
if (mode === 'development') {
return {
// Development-specific configurations
plugins: \[/\* debug plugins \*/\],
};
} else {
return {
// Production-specific configurations
plugins: \[/\* optimization plugins \*/\],
};
}
});
Use a pre-processor plugin
For more advanced cases, you could use the preprocessing plugin unplug-in-preprocessor-directives that provide more explicit syntax for conditional compilation, similar to preprocessor directives in other languages (e.g., C/C++). These plugins allow you to define custom conditions and use directives within your code to include or exclude blocks.
unplugin-preprocessor-directives supports Vite, Rollup, Webpack, Nuxt, Vue CLI, esbuild and Rspack (experimental). You can even define your own custom pre-processor directives.
An older vite-plugin-conditional-compile plugin exists but from v0.2.0 it's basically just a wrapper for unplug-in-preprocessor-directives.
unplugin-preprocessor-directives plugin example
// vite.config.ts
import PreprocessorDirectives from 'unplugin-preprocessor-directives/vite'
export default defineConfig({
plugins: [
PreprocessorDirectives({ /* options */ }),
],
})
// src/index.ts
// #define VERBOSE
...
// #if VERBOSE
console.log('Verbose output version')
// #endif