2

I have a functional AngularJS 1.7 application where all my code is written in TypeScript. But something's always bothered me.

In my app.module.ts file, I have this bit of ugliness:
declare var angular;
in order for the
this.app = angular.module('app'...
to transpile and run.

Things I've tried:

1) Replacing declare var angular; with
import angular from "angular";
transpiles just fine, but then the browser complains
Uncaught (in promise) TypeError: angular_1.default.module is not a function

2) Replacing declare var angular; with
import * as angular from "angular";
also transpiles fine, but the browser gives a similar error:
Uncaught (in promise) TypeError: angular.module is not a function

3) Replacing declare var angular; with
import ng from "angular";
and then using ng.angular.module or ng.module doesn't transpile at all.

I'm sure I've tried some other things, but the only way I've ever been able to get things to work is with that declare var angular;

It all works fine, but that sure smells bad to me. Why do I have to do that? What am I doing wrong? Is there a better way to do it?


Details:

  • Visual Studio 2017/2019
  • TypeScript 3.3
  • SystemJS (not RequireJS)
  • AngularJS 1.7.8
  • @types/angular 1.6.54

package.json

"devDependencies": {
    "@types/angular": "^1.6.54",
    ...
},
"dependencies": {
    "angular": "~1.7.8",
    ...
}

tsconfig.json

{
    "compileOnSave": true,
    "compilerOptions": {
        "baseUrl": ".",
        "paths": {
            "*": [ "node_modules/types/*", "node_modules/*" ],
        },
        "module": "system",
        "target": "es6",
        "sourceMap": true,
        "lib": [ "es6", "dom" ],
        "allowSyntheticDefaultImports": true,
        "outFile": "./app/everything.js",
        "moduleResolution": "node",
        "types": [
            "angular",
            "jquery"
        ]
    },
    "include": ["app/**/*"],
    "exclude": ["node_modules", "lib"],
    "strict": true
}

app.module.ts

declare var angular;
...
export class App {
    app: ng.IModule;

    constructor() {
        this.app = angular.module('app', [
        ...
        ]);
    }

    public run() {
        this.app.run([
        ...
        ]);
        ...
    }

index.html

...
<script src="lib/systemjs/system.js"></script>
<script src="lib/angular/angular.min.js" type="text/javascript"></script>
...
<script src="app/everything.js" type="text/javascript"></script>

<script>
    System.config({
        map: {
            "angular": "lib/angular",
            ...
        },
        packages: {
            "angular": { main: "angular.min.js", defaultExtension: "js" },
            ...
        }
    });
    System.import("app.module")
        .then(function (app) {
            let a = new app.App();
            a.run();

            angular.element(function () {
                angular.bootstrap(document, ['app']);
            });
        });
</script>
2
  • 1
    Please take some time to read How to Ask, especially the part titled "Write a title that summarizes the specific problem". Your current title... doesn't do that :). Commented May 8, 2019 at 20:44
  • Yes, the title wasn't very good. Edited it, and hopefully it's better now. Thanks! Commented May 8, 2019 at 23:44

1 Answer 1

3

Your Typescript types, @types/angular, look for the angular variable to attach their types to.

Normally, you'd have imported angular like this: import angular from "angular" and the types would be applied to the angular variable you just created, and all would work just fine.

However, you are already loading Angular globally in your HTML <script>. In this case, you don't want to import angular from "angular" because then you will have loaded Angular more than once, and things might break.

By writing declare var angular you are simply telling Typescript to trust you that it exists, even though it hasn't seen it be imported into the scope of this file.

This means

  • Typescript won't complain about Angular being undefined.
  • @types/angular will attach to your variable angular.

When you get to runtime, assuming angular does really exist on window, then it will work.

This is a problem that will be required while you are moving to bundlers like Webpack or SystemJS. Once you remove the global Angular from index.html <script>, you can import into your TS files and delete the declare var.

Sign up to request clarification or add additional context in comments.

4 Comments

Hmm. So the 'declare var angular;' is doing the typings as well? I was of the impression that it was simply declaring 'angular' to be such that TypeScript wouldn't do any type checking on it...
I added more detail to my answer.
Thank you for adding more detail! I loaded Angular globally in the HTML because I use other Angular-related things like angular-route and the main Angular.js file needs to be loaded first. They aren't used in my TypeScript code, so I was just loading them instead of importing them. I have since taken the time to add all of those to the SystemJS configuration, then tried importing them into my TypeScript code so SystemJS will load them in the right order, but I get the same error. I find a lot of this baffling. What's the point of the @types/angular/index.d.ts if I can't use it?
There must be something wrong with your imports. Console.log angular and see what you’re getting. Apparently not the object with module as a method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.