What is the importance of including "declare var angular" while working with Typescript and AngularJS?

I've been working on an AngularJS 1.7 application that's coded entirely in TypeScript, and there's always been one thing bothering me.

Within my app.module.ts file, I have this piece of code that doesn't sit right with me:
declare var angular;
This is necessary for the

this.app = angular.module('app'...

to transpile and function correctly.

I've experimented with a few solutions:

1) Switching out declare var angular; with
import angular from "angular";
transpiles without issues, but then the browser throws an error:

Uncaught (in promise) TypeError: angular_1.default.module is not a function

2) Substituting declare var angular; with

import * as angular from "angular";

also transpiles smoothly, but again, leads to a similar browser error:
Uncaught (in promise) TypeError: angular.module is not a function

3) Swapping declare var angular; with
import ng from "angular";
and using ng.angular.module or ng.module fails to transpile altogether.

In the end, the only method that has worked for me is sticking with declare var angular;

While everything functions properly, it just doesn't feel quite right. Why is this step necessary? Am I missing something? Is there a cleaner approach?


Details:

  • Utilizing Visual Studio 2017/2019
  • TypeScript version 3.3
  • SystemJS (not RequireJS)
  • AngularJS version 1.7.8
  • @types/angular version 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>

Answer №1

Your Typescript types, specifically the @types/angular package, rely on the presence of the angular variable to assign their types.

In a typical scenario, you would import Angular like this: import angular from "angular", allowing the types to be applied to the newly created angular variable smoothly.

However, if you are already loading Angular globally in your HTML using a <script> tag, importing it again via import angular from "angular" might cause conflicts and lead to potential issues.

By using declare var angular, you are indicating to Typescript that the angular variable exists somewhere in the global scope, even though it hasn't been explicitly imported into the current file.

This approach ensures that:

  • Typescript won't throw errors about an undefined Angular.
  • The types from @types/angular will still be associated with your angular variable.

At runtime, as long as the angular object is indeed present on the window object, everything should function correctly.

This workaround becomes necessary when transitioning to bundlers like Webpack or SystemJS. Once you eliminate the global Angular script from your index.html, you can proceed to import Angular into your TypeScript files and remove the declare var statement.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Place three images in the center of a div container

Recently, I've been working on some HTML code that utilizes the Angular Material library: <div layout="row" layout-wrap style="background: yellow; "> <div ng-repeat="pro in Products" > <md-card class="cardProduct" > ...

Using a specific type of keys, attempting to set two instances of those keys simultaneously will result in an error of that type

Consider this scenario: type Example = { x: string, y: number } const a: Example = { x: "x", y: 1 } const b: Example = { x: "y", y: 2 } const issue = (keys: (keyof Example)[]) => { keys.forEach(key => { a[key] ...

I need to access the link_id value from this specific actionid and then execute the corresponding function within the Ionic framework

I have a JavaScript code in my TypeScript file. It retrieves the attribute from a span element when it is clicked. I want to store this attribute's value in a TypeScript variable and then call a TypeScript function. Take a look at my ngOnInit method, ...

New Announcement: Implementing Flex Images in AngularJS

Issue with Image Resizing Solution I am currently developing a responsive Angular.js website and was looking to use different resolution images based on browser width. After some research, I came across a solution called Picturefill.js, which seemed perfe ...

Utilizing AngularJS to create a cascading drop-down menu

I am attempting to create a cascading drop-down menu where the second drop-down will display options related to the item selected in the first drop-down box. However, even after trying some sample code, the second drop-down remains empty. Here is the HTML ...

Show just a single error message if there are two validation errors present

In my AngularJS timepicker, users can choose multiple time segments for each day. The code has validation to detect duplicates and overlapping time segments. For example, entering 11:00am - 12:00am twice will trigger two error messages: 'Overlapping t ...

Convert HTML templates into JavaScript on the client side using Angular framework along with Browserify, Babel, ES2015, and Gulp

Having trouble with my Browserify Angular configuration file, specifically when using require() to load HTML templates. I attempted to use stringify or browserify-ng-html2js in the transform() function, but it resulted in multiple transform calls in my gul ...

Come back to Angular 2 on your return function

Having a problem with an asynchronous function. There is a service that retrieves data from a Firebase database. One of the functions returns a value: historialDeConsumi() { this.item = this.af.database.object('/users/' + this.uid + '/a ...

The loop within a loop is causing excessive lag and is on the verge of crashing the

I need help with optimizing the performance of my app that retrieves json data. The json file contains nearly one thousand words structured like this: {"THEMES":{"THEME1":["ITEM1","ITEM2","ITEM3"],"THEME2":["ITEM1",...]...}} The size of the file is aroun ...

Issues with navigation in React Native Typescript

Currently, I am in the process of developing a new React Native Expo project utilizing TypeScript. I have been attempting to configure navigation following the guidance provided in React Native's TypeScript documentation. However, upon running and sim ...

How to pass information from a detail page to a master page using Ionic and AngularJS?

When working with Ionic (or AngularJS in general), I am faced with a situation where I have Master/Detail pages. In the Detail page, I select some data and need to pass this data back to the Master controller. Can anyone provide guidance on how to accompl ...

The function(result) is triggered when an http.get request is made

Can anyone help me figure out why my function is jumping after completing the request? It seems to be skipping over .then(function(result){ }. I suspect that the issue might be related to the <a> element with an onclick attribute containing an href ...

How to set a cookie using an AJAX request in AngularJS

My goal is to create a cookie that holds a token value obtained through an Ajax request in AngularJS. To start, I checked the outcome of the request by displaying the token value on my HTML page -> {{myToken}}. The output was accurate. Now, I am attemptin ...

The art of transforming properties into boolean values (in-depth)

I need to convert all types to either boolean or object type CastDeep<T, K = boolean> = { [P in keyof T]: K extends K[] ? K[] : T[P] extends ReadonlyArray<K> ? ReadonlyArray<CastDeep<K>> : CastDeep<T[P]> ...

Unable to link to '' because it is not recognized as a valid attribute of '' in Angular 2

I encountered an exception while working on my Angular 2 project and I'm struggling to figure out the cause. Below is the snippet of my code: ts: import {Component} from "@angular/core"; import {GridOptions} from "ag-grid"; import {RedComponentComp ...

How to effectively inject moment.js dependency into AngularJS?

In my app.js file, I've defined my app module like this: var myApp = angular.module('myApp', ['angularMoment']); Next, in my controller, I'm trying to use the moment() function: myApp.controller('myComtroller', [& ...

Ensure that a variable adheres to the standards of a proper HTML template

I'm struggling with a problem in my Angular application. I need to ensure that a TypeScript variable is a valid HTML template to compile successfully, like this: let v = '<div>bla…</div>' However, if the variable contains inco ...

Tips on retrieving an object in a JavaScript function through an HTML event

The js function code is as follows: //Assigning the index of theater array to a variable $scope.setTheaterValue = function(name) { var index = $scope.path.map(function(s){return s.name}).indexOf(name); $scope.path = $scope.path[index]. ...

Distribute your SolidJS Typescript components on npm

Recently, I developed a SolidJS TypeScript UI component and successfully published it to the npm registry. The project structure is organized as follows: |-app |-index.html |-src |-index.tsx |-App.tsx |-utils |- ... |-com ...

Spreading angular attribute directives while compiling element directives

I am facing challenges in writing a custom element directive. This directive, which I'll name myElement, generates multiple textarea or input fields based on certain parameters and applies ngModels to those fields. In addition, I want to be able to s ...