Idiomatic Conditional TypeScript compilation in Angular

Viewed 523

I'm using Angular 9 and I have some code like this:

(features.ts, autogenerated:)
// AUTO-GENERTATED FILE. DO NOT EDIT!
export const Features = {
  // Whether to reveal our Secret New Feature to the world
  ENABLE_SECRET_FEATURE: 1
};

(mycode.ts, app code)
import { Features } from 'generated/features.ts';

function doSomething() {
  if (Features.ENABLE_SECRET_FEATURE) {
    doAIBlockChainARThing();
  } else {
    doSomeBoringOldCRUDThing();
  }
}

I'd like the code emitted to be EITHER

function doSomething() {
    doAIBlockChainARThing();
}

OR

function doSomething() {
    doSomeBoringOldCRUDThing();
}

but not both.

Is there an invocation of ng build that would make this happen? I know uglify can sort of do this and Closure Compiler certainly can. My current invocation is: ng build --aot=true --stats-json --buildOptimizer=true --optimization=true --prod

After some experimentation I have see that the terser settings in Angular prod builds do what I want if I have code like:

const SECRET_FEATURE = true;
if (SECRET_FEATURE) {
  // code here is emitted
} else {
  // code here is NOT emitted
}

However if I try to do something like:

import {SECRET_FEATURE} from 'my-features';

if (SECRET_FEATURE) { // this conditional is emitted
  // this code is emitted
} else {
  // this code is also emitted
}

My thought is I'll have to use something like https://www.npmjs.com/package/tsickle for better dead-code elimination, along with a custom WebPack config to call it. I was hoping for a more Angular-centric path just so I don't have to create/document a lot of custom machinery for future engineers.

3 Answers

I've just tried on a fresh angular 9 project build with ng build --prod

With the following code, in a component's class

import { Features } from 'generated/features.ts';

doSomething() {
  if (Features.ENABLE_SECRET_FEATURE) {
    console.log('AAAA');
  } else {
    console.log('BBBB')();
  }
}

the compiled code is what you expect (the else part is not emitted)

doSomething() {console.log('AAAA');}

If you have other methods calls in you if/else, this is a bit different

For instance, with the code below

import { Features } from 'generated/features.ts';

   doSomething() {
    if (Features.ENABLE_SECRET_FEATURE) {
      this.doAIBlockChainARThing();
    } else {
      this.doSomeBoringOldCRUDThing();
    }
  }

  private doAIBlockChainARThing()
  {
    console.log('AAAAAAAAA');
  }

  private doSomeBoringOldCRUDThing()
  {
    console.log('BBBBB');
  }

the compiled code is

doSomething(){this.doAIBlockChainARThing()}
doAIBlockChainARThing(){console.log("AAAAAAAAA")}
doSomeBoringOldCRUDThing(){console.log("BBBBB")}}

So the else part is not generated, but unused private methods are not dropped by terser either.

You could use your if test again in the specific method to have no secret code generated at all, but it's not very convenient

  private doAIBlockChainARThing()
  {
    if (Features.ENABLE_SECRET_FEATURE)
    console.log('AAAAAAAAA');
  }
Related