Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Using Import = Require Syntax With TypeScript 2.2 In Angular 2.4.9

By Ben Nadel on

When using TypeScript, importing non-TypeScript modules has a somewhat irritating "* as" syntax. For example, if you wanted to import lodash into your TypeScript application, you might use the following import statement:

import * as _ from "lodash";

I think earlier versions of TypeScript allowed for a simplified default import syntax; but, I believe that the TypeScript team dropped it in favor of the more explicit syntax. Over the weekend, however, I was reading through Basarat Ali Syed's book, "TypeScript Deep Dive", when I saw him use a very simple "import =" expression that I had not seen before. Though somewhat inconsistent with the rest of my import statements, the simplified syntax makes it quite nice. And, something I thought would be worth sharing.

Run this demo in my JavaScript Demos project on GitHub.

Ironically, the "import =" syntax is right there in the Modules portion of the TypeScript documentation. However, I have yet to sit down and actually "read the manual" on TypeScript. So, it was just lucky that I discovered it in Bararat's book:

import _ = require( "lodash" );

It seems a little unusual to mash together the ES6 module syntax with the CommonJS module syntax. But, it's still better than the "* as" syntax mentioned above.

To see this "import = require" syntax in action, I put together a small Angular 2 demo in which I import lodash and use it to map one array of values onto another array of values:

// Import the core angular services.
// --
// NOTE: When I'm including the LODASH library, I'm using the "import =" syntax since
// the lodash library has a single top-level export.
import _ = require( "lodash" );
import { Component } from "@angular/core";

interface Friend {
	id: number;
	name: string;
}

@Component({
	selector: "my-app",
	styleUrls: [ "./app.component.css" ],
	template:
	`
		<p>
			<strong>Friends:</strong> {{ names | json }}
		</p>
	`
})
export class AppComponent {

	public friends: Friend[];
	public names: string[];


	// I initialize the app component.
	constructor() {

		this.friends = [
			{ id: 1, name: "Kim" },
			{ id: 2, name: "Sarah" },
			{ id: 3, name: "Joanna" },
			{ id: 4, name: "Libby" }
		];
		this.names = this.pluckNames( this.friends );

	}


	// ---
	// PRIVATE METHODS.
	// ---


	// I return the names property as an array, plucked from the given collection.
	private pluckNames( collection: Friend[] ) : string[] {

		// NOTE: I need to explicitly cast the return value here because lodash
		// overloads the .map() method instead of having an explicit "pluck" method.
		// As such, the definition file gets confused on the return type.
		return( <string[]>_.map( collection, "name" ) );

	}

}

The example is silly; but, the TypeScript compiler executes without any error. And, when we run the above code, we get the following output:

Using the import equals require syntax in TypeScript 2.

As you can see, this "import = require" syntax works and, is in my opinion, much nicer than the "* as" syntax. Even though it does break the consistent form of the other import statements.

Now, since I've started using Webpack to build my Angular 2 demos, I've started creating a "vendors" file for things like lodash, so that Webpack's CommonsChunkPlugin plugin can properly isolate code that changes at a different rate. For this demo, my "main.vendor.ts" file looks like this:

// Import these libraries for their side-effects.
// --
// CAUTION: As you add more "import" statements to your application code, you will have
// to come back to this file and add those imports here as well (otherwise that imported
// content may get bundled with your main application bundle, not your vendor bundle.
import "@angular/core";
import "@angular/platform-browser-dynamic";
import "lodash";

As you can see, I am importing lodash using the TypeScript "side effect" syntax. This is compatible with the aforementioned "import = require" syntax and Webpack is able to successfully siphon the lodash library off into the vendor file.

I personally find this syntax, though not without its drawbacks, to be more pleasant than the "* as" syntax that I've been using up until now. You can technically use this syntax with TypeScript files as well (if they use the "export =" syntax); however, I see this primarily as a way to consume non-TypeScript files, like lodash, in a TypeScript application.

NOTE: You still need to provide type definition files for your non-TypeScript modules; or, TypeScript will complain that they have an implicit type of "any".



Reader Comments

There is a typescript compiler option called allowSyntheticDefaultImports which allows you to import without the strange * syntax.

Though I'm not sure if it's a best practice to use it.

@Leon,

Do you know, I've tried to use that several times, both when I was working with System.js and I think when i was using the TypeScript compiler and I could never get it to work. The default export was always coming back as undefined. If you could point me towards a working example anywhere, I would be much appreciated!

Hmm... I personally prefer `import * as _ from "lodash"`. Since more consistent with the TypeScript default one, rather than CommonJs like. Thank you for showing the alternative though and thank you for keeping the blog post short.

I get ERROR in /Projects/shopping-list/src/app/app.component.ts (5,1): Import assignment cannot be used when targeting ECMAScript 2015 modules. Consider using 'import * as ns from
"mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.

@Demisx,

Yeah, I definitely have an emotional problem with it being a different syntax. Seems, somehow, dirty. But, I also find the "* as foo" to be different than my other quasi-destructuring statements... so, to some degree, it's 6 of one, half-a-dozen of the other. I'm still trying to working around in my head.

@Jesse,

What version of TypeScript are you using? It's possible that the "require()" syntax was added later. I am not entirely sure of its history.

@Leon,

Interesting - I was able to download and run your demo successfully. But, when I try to do it in my own demo:

https://github.com/bennadel/JavaScript-Demos/tree/master/demos/import-require-angular2

... it still doesn't work. Without the "allowSyntheticDefaultImports" flag, the tsc CLI gives me the following error:

> error TS1192: Module '"/Users/ben/Sites/bennadel.com/projects/javascript_demos/vendor/angular2/2.4.9-webpack/node_modules/@types/lodash/index"' has no default export.

... and if I add the "allowSyntheticDefaultImports" flag, the tsc CLI doesn't complain, but the import "_" value is undefined in the app runtime.

There must be some fundamental difference between our two configurations / compilers; but, I am not knowledgeable enough to see it (mostly I know enough about TypeScript and the tsc compiler to just "get it working" and not much more than that).

It looks like this flag may also be failing in ts-node (which I can confirm):

https://github.com/TypeStrong/ts-node/issues/86

Ah, and from this ticket, it looks like this might not be a TypeScript issue at all, but a loader issue: https://github.com/Microsoft/TypeScript/issues/7518

> allowSyntheticDefaultImports does not change the output.
> All what allowSyntheticDefaultImports is tells the compiler that at
> runtime your loader (e.g. SystemJs) will perform this operation of
> mapping modules to default imports, and thus avoids the error at
> build time.

... so it could be that the my Webpack build is somehow not making it available? I'm super new to Webpack, so this is quite a bit over my head.