Using Import = Require Syntax With TypeScript 2.2 In Angular 2.4.9
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:

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
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!
@Ben
I made a quick demo repo for you
https://github.com/leon/demo-ng-lodash
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.
@Leon,
Very cool - I will see if I can get this running locally!
@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.