Skip to main content
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Andy Matthews and Todd Sharp and Jason Dean and Simon Free
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Andy Matthews ( @commadelimited ) Todd Sharp ( @cfsilence ) Jason Dean ( @JasonPDean ) Simon Free ( @simonfree )

Using Import = Require Syntax With TypeScript 2.2 In Angular 2.4.9

By 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".

Want to use code from this post? Check out the license.

Reader Comments

7 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.

15,377 Comments

@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!

1 Comments

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.

1 Comments

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.

15,377 Comments

@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.

15,377 Comments

@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.

15,377 Comments

@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.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel