Importing lodash into angular2 + typescript application
Asked Answered
C

23

303

I am having a hard time trying to get the lodash modules imported. I've setup my project using npm+gulp, and keep hitting the same wall. I've tried the regular lodash, but also lodash-es.

The lodash npm package: (has an index.js file in the package root folder)

import * as _ from 'lodash';    

Results in:

error TS2307: Cannot find module 'lodash'.

The lodash-es npm package: (has a default export in lodash.js i the package root folder)

import * as _ from 'lodash-es/lodash';

Results in:

error TS2307: Cannot find module 'lodash-es'.   

Both the gulp task and webstorm report the same issue.

Funny fact, this returns no error:

import 'lodash-es/lodash';

... but of course there is no "_" ...

My tsconfig.json file:

{
  "compilerOptions": {
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "node_modules"
  ]
}

My gulpfile.js:

var gulp = require('gulp'),
    ts = require('gulp-typescript'),
    uglify = require('gulp-uglify'),
    sourcemaps = require('gulp-sourcemaps'),
    tsPath = 'app/**/*.ts';

gulp.task('ts', function () {
    var tscConfig = require('./tsconfig.json');
    
    gulp.src([tsPath])
        .pipe(sourcemaps.init())
        .pipe(ts(tscConfig.compilerOptions))
        .pipe(sourcemaps.write('./../js'));
});

gulp.task('watch', function() {
    gulp.watch([tsPath], ['ts']);
});

gulp.task('default', ['ts', 'watch']);

If I understood correctly, moduleResolution:'node' in my tsconfig should point the import statements to the node_modules folder, where lodash and lodash-es are installed. I've also tried lots of different ways to import: absolute paths, relative paths, but nothing seems to work. Any ideas?

If necessary I can provide a small zip file to illustrate the problem.

Cosby answered 7/1, 2016 at 16:28 Comment(6)
I ran into this problem too. The lodash library does not have typecript definitions included in modular format so the import statements do not work. The only work around now it seems is make a script reference to lodash in your index.html file then reference the lodash.d.ts in your typescript files. hopefully this will be fixed soon. if there is another work around for this i would like to hear it.Hardily
the zip file would be great. But it looks like you are not using any module loader (like jspm or webpack) ? How are you loading Angular, via script tags? better post the html as well. I recommend you use webpack as module loader, see here an example -> github.com/jhades/angular2-library-example/tree/master/examples/… and this is a minimal starter -> github.com/jhades/ng2-webpack-minimalAlignment
See this link: github.com/DefinitelyTyped/DefinitelyTyped/issues/4889Hardily
I ended up just adding this to my mail ts file: /// <reference path="../typings/tsd.d.ts" />Cosby
As of today none of the above works. I am using the angular.2.4.4 stack.Sievers
Does this answer your question? Correct way of importing and using lodash in AngularOstiole
B
505

Here is how to do this as of Typescript 2.0: (tsd and typings are being deprecated in favor of the following):

$ npm install --save lodash

# This is the new bit here: 
$ npm install --save-dev @types/lodash

Then, in your .ts file:

Either:

import * as _ from "lodash";

Or (as suggested by @Naitik):

import _ from "lodash";

I'm not positive what the difference is. We use and prefer the first syntax. However, some report that the first syntax doesn't work for them, and someone else has commented that the latter syntax is incompatible with lazy loaded webpack modules. YMMV.

Edit on Feb 27th, 2017:

According to @Koert below, import * as _ from "lodash"; is the only working syntax as of Typescript 2.2.1, lodash 4.17.4, and @types/lodash 4.14.53. He says that the other suggested import syntax gives the error "has no default export".

Blucher answered 29/8, 2016 at 15:30 Comment(19)
I can confirm that this works when using typescript 2.0.3. Indeed removing typings in favor of @types npm package is much cleaner.Welcher
import * as _ from "lodash"; wasn't working for me but import _ from "lodash"; does.Stunt
A word of warning, I've found the import _ from "lodash" syntax to be incompatible with lazy loaded webpack modules. Not sure why, I haven't investigated in detail.Amberlyamberoid
Thanks @TomMakin for the word of warning, and thanks Naitik for pointing out the alternative syntax.Blucher
@Blucher How do I set _ as a global constant so that I only import once e.g. in the root? I would rather not have to write import * as _ from "lodash"; at the top of every .ts file, I'd just like _ to be there.Wedge
@GFoley83: Someone asked something similar about React: github.com/Microsoft/TypeScript/issues/3180 Based on that issue, this is considered somewhat of an anti-pattern once you are using typical modules. However, I believe you could do it by having the lodash script loaded separately as part of your page load, putting it in the global scope. Then, you should be able to do this in a file that is included by all your files. /// <reference path="../node_modules/@types/index.d.ts"/> That declares _ as part of the global scope.Blucher
@Damiani I disagree, I think importing _ and referencing functions off that is a lot cleaner than having function references - as you can see that the function came from lodash. If you're using tree shaking it shouldn't bear a difference on the production builds.Acre
Can you be more specific @Toolkit?Blucher
Can you be more specific @030?Blucher
Typings is being deprecated too? Man, I just switched to that from TSD.Cooksey
@Blucher import * as _ from "lodash"; works for me. I'm using lodash 4.17.4 and @types/lodash 4.14.53. With Typescript 2.2.1. Importing without "as" results in "has no default export".Undenominational
import _ from "lodash"; doesn't work anymore in 2.0. You have to use import * as _ from "lodash";Conservator
Uncaught ReferenceError: exports is not definedMcclure
This should be used only in development, not production, so use save-dev: npm install --save-dev @types/lodash If you're seeing weird issues and bugs, try this: npm install --save-dev @types/[email protected]Linette
Adding confirmation that I could only get this by using the former syntax import * as _ from "lodash" -- i am using tsc ^2.2.2 and webpack ^2.4.1 with ts-loader ^2.0.3 -- i am betting something changes in one dependency along the wayCrampon
npm install --save-dev @types/lodash will be the wise choiceCyprinoid
As of Angular 6.1.2 this works fine: import * as _ from 'lodash'; using typescript 2.7.2 and lodash 4.17.10 with @types/lodash 4.14.116Hardener
To decrease lodash bundle size .. I found this article very helpfulBerck
import * as _ from "lodash" imports all non-default exports from lodash and puts them in an object named _. That means this will still work even if lodash doesn't have a default export.Expeller
A
70

Update September 26, 2016:

As @Taytay's answer says, instead of the 'typings' installations that we used a few months ago, we can now use:

npm install --save @types/lodash

Here are some additional references supporting that answer:

If still using the typings installation, see the comments below (by others) regarding '''--ambient''' and '''--global'''.

Also, in the new Quick Start, config is no longer in index.html; it's now in systemjs.config.ts (if using SystemJS).

Original Answer:

This worked on my mac (after installing Angular 2 as per Quick Start):

sudo npm install typings --global
npm install lodash --save 
typings install lodash --ambient --save

You will find various files affected, e.g.

  • /typings/main.d.ts
  • /typings.json
  • /package.json

Angular 2 Quickstart uses System.js, so I added 'map' to the config in index.html as follows:

System.config({
    packages: {
      app: {
        format: 'register',
        defaultExtension: 'js'
      }
    },
    map: {
      lodash: 'node_modules/lodash/lodash.js'
    }
  });

Then in my .ts code I was able to do:

import _ from 'lodash';

console.log('lodash version:', _.VERSION);

Edits from mid-2016:

As @tibbus mentions, in some contexts, you need:

import * as _ from 'lodash';

If starting from angular2-seed, and if you don't want to import every time, you can skip the map and import steps and just uncomment the lodash line in tools/config/project.config.ts.

To get my tests working with lodash, I also had to add a line to the files array in karma.conf.js:

'node_modules/lodash/lodash.js',
Annabelle answered 16/4, 2016 at 21:29 Comment(10)
I see that this resolves my TypeScript issues, but loading the page in a browser I still see the error that it can't find module lodash. It looks like it's making a failed xhr request to '/lodash' instead of node_modules.Spatz
@zack did you miss map: { lodash: 'node_modules/lodash/lodash.js' } in the System.config ?Slink
For me it works only with import * as _ from 'lodash';Mariselamarish
What does the --ambient parameter do?Damali
...and why do we need to write import * as _ from 'lodash' instead of import _ from 'lodash' ?Damali
@Damali --ambient is a former for --global. The latter is used in 1.x and moving forward. Though, I don't think the latest lodash 4.x will compile as global module like that.Gurgle
Yes, lodash typings won't save with --global flag because it's an external module.Scrunch
@Toolkit I see you removed a similar comment from a similar answer. Is it still true here?Annabelle
eventually this works for me,thanks. I think the most important step is map: { lodash: 'node_modules/lodash/lodash.js' }Foreworn
use this way, I cannot load nth, anybody had the same problem?Foreworn
A
27

First things first

npm install --save lodash

npm install -D @types/lodash

Load the full lodash library

//some_module_file.ts
// Load the full library...
import * as _ from 'lodash' 
// work with whatever lodash functions we want
_.debounce(...) // this is typesafe (as expected)

OR load only functions we are going to work with

import * as debounce from 'lodash/debounce'
//work with the debounce function directly
debounce(...)   // this too is typesafe (as expected)


UPDATE - March 2017

I'm currently working with ES6 modules, and recently i was able to work with lodash like so:

// the-module.js (IT SHOULD WORK WITH TYPESCRIPT - .ts AS WELL) 
// Load the full library...
import _ from 'lodash' 
// work with whatever lodash functions we want
_.debounce(...) // this is typesafe (as expected)
...

OR import specific lodash functionality:

import debounce from 'lodash/debounce'
//work with the debounce function directly
debounce(...)   // this too is typesafe (as expected)
...

NOTE - the difference being * as is not required in the syntax


References:

enter image description here

Good Luck.

Adulterous answered 19/11, 2016 at 5:40 Comment(6)
"Import assignment cannot be used when targeting ECMAScript 2015 modules"Shaniqua
@Toolkit. Thank you for pointing it out. I have updated the answer. Please check if this solutions works and mark appropriately.Adulterous
import debounce from 'lodash/debounce' yields TS1192: Module node_modules/@types/lodash/debounce has no default export when "allowSyntheticDefaultImports": falseIchnite
@Ichnite i just did npm i --save @types/lodash and peeked into the @types/lodash folder and found the file debounce.d.ts to be there and thus it should work. You can checkout more info on tsconfig.json here typescriptlang.org/docs/handbook/tsconfig-json.html. I've also included a snapshot from there in the answer. Good Luck...Adulterous
I stand by my previous comment. There is not currently a default export in the types file, so this does not work with allowSyntheticDefaultImports false.Ichnite
@Ichnite Also note that adding the "allowSyntheticDefaultImports": true compiler option in your tsconfig.json file might be needed to avoid any error.Adulterous
M
23

Step 1: Modify package.json file to include lodash in the dependencies.

  "dependencies": {
"@angular/common":  "2.0.0-rc.1",
"@angular/compiler":  "2.0.0-rc.1",
"@angular/core":  "2.0.0-rc.1",
"@angular/http":  "2.0.0-rc.1",
"@angular/platform-browser":  "2.0.0-rc.1",
"@angular/platform-browser-dynamic":  "2.0.0-rc.1",
"@angular/router":  "2.0.0-rc.1",
"@angular/router-deprecated":  "2.0.0-rc.1",
"@angular/upgrade":  "2.0.0-rc.1",
"systemjs": "0.19.27",
"es6-shim": "^0.35.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12",
"lodash":"^4.12.0",
"angular2-in-memory-web-api": "0.0.7",
"bootstrap": "^3.3.6"  }

Step 2:I am using SystemJs module loader in my angular2 application. So I would be modifying the systemjs.config.js file to map lodash.

(function(global) {

// map tells the System loader where to look for things
var map = {
    'app':                        'app', // 'dist',
    'rxjs':                       'node_modules/rxjs',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    '@angular':                   'node_modules/@angular',
    'lodash':                    'node_modules/lodash'
};

// packages tells the System loader how to load when no filename and/or no extension
var packages = {
    'app':                        { main: 'main.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' },
    'lodash':                    {main:'index.js', defaultExtension:'js'}
};

var packageNames = [
    '@angular/common',
    '@angular/compiler',
    '@angular/core',
    '@angular/http',
    '@angular/platform-browser',
    '@angular/platform-browser-dynamic',
    '@angular/router',
    '@angular/router-deprecated',
    '@angular/testing',
    '@angular/upgrade',
];

// add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
    packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});

var config = {
    map: map,
    packages: packages
}

// filterSystemConfig - index.html's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }

System.config(config);})(this);

Step 3: Now do npm install

Step 4: To use lodash in your file.

import * as _ from 'lodash';
let firstIndexOfElement=_.findIndex(array,criteria);
Marilyn answered 16/5, 2016 at 10:35 Comment(1)
How does your solution take care of the TypeScript typings? The npm lodash package does not seem to include the .d.ts files.Elbow
P
14

Since Typescript 2.0, @types npm modules are used to import typings.

# Implementation package (required to run)
$ npm install --save lodash

# Typescript Description
$ npm install --save @types/lodash 

Now since this question has been answered I'll go into how to efficiently import lodash

The failsafe way to import the entire library (in main.ts)

import 'lodash';

This is the new bit here:

Implementing a lighter lodash with the functions you require

import chain from "lodash/chain";
import value from "lodash/value";
import map from "lodash/map";
import mixin from "lodash/mixin";
import _ from "lodash/wrapperLodash";

source: https://medium.com/making-internets/why-using-chain-is-a-mistake-9bc1f80d51ba#.kg6azugbd

PS: The above article is an interesting read on improving build time and reducing app size

Perfect answered 13/10, 2016 at 8:10 Comment(2)
For TSC this is great, however it gives other issues with bundlers. any chance you managed to get this working through rollup? aurelia-cli also gives issues with it :(. Rollup error: 'default' is not exported by node_modules\lodash\kebabCase.js Aurelia cli error: no such file or directory, open '/experimental\au-proj\node_modules\lodash\lodash\kebabCase.js'Costumier
Seems the @types/lodash doesn't support the lighter syntax yet. error TS1192: Module '"node_modules/@types/lodash/chain/index"' has no default export. And so forth for other modules attempting the shorter import chain from "lodash/chain" importsTague
D
10

I successfully imported lodash in my project with the following commands:

npm install lodash --save
typings install lodash --save

Then i imported it in the following way:

import * as _ from 'lodash';

and in systemjs.config.js i defined this:

map: { 'lodash' : 'node_modules/lodash/lodash.js' }

Damali answered 13/7, 2016 at 16:31 Comment(0)
I
8

I had exactly the same problem, but in an Angular2 app, and this article just solve it: https://medium.com/@s_eschweiler/using-external-libraries-with-angular-2-87e06db8e5d1#.p6gra5eli

Summary of the article:

  1. Installing the Library npm install lodash --save
  2. Add TypeScript Definitions for Lodash tsd install underscore
  3. Including Script <script src="node_modules/lodash/index.js"></script>
  4. Configuring SystemJS System.config({ paths: { lodash: './node_modules/lodash/index.js'
  5. Importing Module import * as _ from ‘lodash’;

I hope it can be useful for your case too

Irresponsible answered 29/2, 2016 at 19:57 Comment(1)
tsd is deprecated now. You should use typingsTalbot
S
7

Another elegant solution is to get only what you need, not import all the lodash

import {forEach,merge} from "lodash";

and then use it in your code

forEach({'a':2,'b':3}, (v,k) => {
   console.log(k);
})
Sneakers answered 22/12, 2016 at 9:16 Comment(10)
Does this actually work? I've tried this and it doesn't seem to change the size of the bundle.Neurosurgery
Maybe not back then, but now yes. It's tree shaking at it's bestSneakers
@Sneakers maybe i'm doing it wrong but tried with angular cli 1.4.4 tree shaking didn't workKiakiah
@Kiakiah can you post the error? Is it connected to lodash?Sneakers
@Sneakers there is no error, just no tree shaking. It loads the whole library not forEach and merge methods.Kiakiah
@Kiakiah that's odd. I build with prod and aot with no issues. Can you try adding types ? npm i --save @types/lodash ? I don't use them but it might solve your issueSneakers
@Sneakers Again, it is not about issues with compilation. It is about tree shaking not working when importing like this. Do you understand what the effect of tree shaking is supposed to be?Trinidad
@fnt that problem is long gone since typescript and ng updatesSneakers
@Sneakers What problem? The discussion here is not about the OP's issue, it is about tree shaking.Trinidad
Yes @fnt. Shaking. It used to fail a while ago for lodash types not it worksSneakers
C
4

If anyone else runs into this issue and none of the above solutions work due to "Duplicate identifier" issues, run this:

npm install typings --global

With older versions of typings things mess up and you'll get a bunch of "Duplicate identifier" issues. Also you don't need to use --ambient anymore as far as I could tell.

So once typings is up to date, this should work (using the Angular 2 quickstart).

Run:

npm install lodash --save 
typings install lodash --save

First, add this to systemjs.config.js:

'lodash':                     'node_modules/lodash/lodash.js'

Now you can use this in any file: import * as _ from 'lodash';

Delete your typings folder and run npm install if you're still having issues.

Caligula answered 17/8, 2016 at 14:49 Comment(0)
R
4

Please note that npm install --save will foster whatever dependency your app requires in production code.
As for "typings", it is only required by TypeScript, which is eventually transpiled in JavaScript. Therefore, you probably do not want to have them in production code. I suggest to put it in your project's devDependencies instead, by using

npm install --save-dev @types/lodash

or

npm install -D @types/lodash

(see Akash post for example). By the way, it's the way it is done in ng2 tuto.

Alternatively, here is how your package.json could look like:

{
    "name": "my-project-name",
    "version": "my-project-version",
    "scripts": {whatever scripts you need: start, lite, ...},
    // here comes the interesting part
    "dependencies": {
       "lodash": "^4.17.2"
    }
    "devDependencies": {
        "@types/lodash": "^4.14.40"
    }
}

just a tip

The nice thing about npm is that you can start by simply do an npm install --save or --save-dev if you are not sure about the latest available version of the dependency you are looking for, and it will automatically set it for you in your package.json for further use.

Restive answered 22/11, 2016 at 22:26 Comment(0)
R
4

Partial import from lodash should work in angular 4.1.x using following notation:

let assign = require('lodash/assign');

Or use 'lodash-es' and import in module:

import { assign } from 'lodash-es';
Recurved answered 23/5, 2017 at 7:25 Comment(1)
import { assign } from 'lodash-es'; seems to still import the whole library (judging by bundle size)Towline
C
3

I had created typings for lodash-es also, so now you can actually do the following

install

npm install lodash-es -S
npm install @types/lodash-es -D

usage

import kebabCase from "lodash-es/kebabCase";
const wings = kebabCase("chickenWings");

if you use rollup, i suggest using this instead of the lodash as it will be treeshaken properly.

Costumier answered 5/1, 2017 at 22:11 Comment(0)
W
2

Install via npm.

$ npm install lodash --save

Now, import in the file:

$ import * as _ from 'lodash';

ENV:

Angular CLI: 1.6.6
Node: 6.11.2
OS: darwin x64
Angular: 5.2.2
typescript: 2.4.2
webpack: 3.10.0

Wallack answered 12/2, 2018 at 5:19 Comment(0)
N
1
  1. Install lodash

sudo npm install typings --global npm install lodash --save typings install lodash --ambient --save

  1. In index.html, add map for lodash:

System.config({ packages: { app: { format: 'register', defaultExtension: 'js' } }, map: { lodash: 'node_modules/lodash/index.js' } });

  1. In .ts code import lodash module

import _ from 'lodash';

Nauru answered 24/4, 2016 at 21:49 Comment(1)
It is throwing error: $ typings install lodash --ambient --save typings ERR! message https://api.typings.org/entries/npm/lodash/versions/latest responded with 407, expected it to equal 200Considerate
L
1

I am using ng2 with webpack, not system JS. Steps need for me were:

npm install underscore --save
typings install dt~underscore --global --save

and then in the file I wish to import underscore into:

import * as _ from 'underscore';
Lentic answered 16/6, 2016 at 10:35 Comment(0)
B
1

Managing types via typings and tsd commands is ultimately deprecated in favor of using npm via npm install @types/lodash.

However, I struggled with "Cannot find module lodash" in import statement for a long time:

import * as _ from 'lodash';

Ultimately I realized Typescript will only load types from node_modules/@types start version 2, and my VsCode Language service was still using 1.8, so the editor was reporting errors.

If you're using VSCode you'll want to include

"typescript.tsdk":  "node_modules/typescript/lib"

in your VSCode settings.json file (for workspace settings) and make sure you have typescript version >= 2.0.0 installed via npm install [email protected] --save-dev

After that my editor wouldn't complain about the import statement.

Buchan answered 18/9, 2016 at 23:9 Comment(0)
A
1

if dosen't work after

$ npm install lodash --save 
$ npm install --save-dev @types/lodash

you try this and import lodash

typings install lodash --save
Annates answered 2/8, 2017 at 11:12 Comment(0)
U
0

Install all thru terminal:

npm install lodash --save
tsd install lodash --save

Add paths in index.html

<script>
    System.config({
        packages: {
            app: {
                format: 'register',
                defaultExtension: 'js'
            }
        },
        paths: {
            lodash: './node_modules/lodash/lodash.js'
        }
    });
    System.import('app/init').then(null, console.error.bind(console));
</script>

Import lodash at the top of the .ts file

import * as _ from 'lodash'
Uproar answered 5/3, 2016 at 21:40 Comment(2)
tsd is deprecated now. You should use typingsTalbot
typings is deprecated now. You should use @typesGasconade
E
0

I'm on Angular 4.0.0 using the preboot/angular-webpack, and had to go a slightly different route.

The solution provided by @Taytay mostly worked for me:

npm install --save lodash
npm install --save @types/lodash

and importing the functions into a given .component.ts file using:

import * as _ from "lodash";

This works because there's no "default" exported class. The difference in mine was I needed to find the way that was provided to load in 3rd party libraries: vendor.ts which sat at:

src/vendor.ts

My vendor.ts file looks like this now:

import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';

import 'rxjs';
import 'lodash';

// Other vendors for example jQuery, Lodash or Bootstrap
// You can import js, ts, css, sass, ...
Exactitude answered 6/5, 2017 at 2:28 Comment(1)
You should really dial down on importing the whole rxjs and everything from lodash...Builtup
M
0

Maybe it is too strange, but none of the above helped me, first of all, because I had properly installed the lodash (also re-installed via above suggestions).

So long story short the issue was connected with using _.has method from lodash.

I fixed it by simply using JS in operator.

Munshi answered 15/6, 2018 at 16:23 Comment(0)
U
0

npm install --save @types/lodash

https://www.npmjs.com/package/@types/lodash

Universalism answered 12/10, 2019 at 21:4 Comment(0)
D
-1

try >> tsd install lodash --save

Dewan answered 19/2, 2016 at 5:0 Comment(4)
tsd is deprecated now. You should use typingsTalbot
typings is deprecated now. You should use @types.. :)Superphysical
i am waiting for another deprecation comment ;)Strive
deprecation is deprecated. err..don't blame me. u asked for that one ;-)Seymour
P
-1

You can also go ahead and import via good old require, ie:

const _get: any = require('lodash.get');

This is the only thing that worked for us. Of course, make sure any require() calls come after imports.

Portraiture answered 1/6, 2018 at 4:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.