How to use lodash.mixin in TypeScript

瘦欲@ 提交于 2020-01-03 13:01:39

问题


My team is evaluating switching some of our files to TypeScript from JavaScript, and we make extensive use of some custom mixin methods in our code. From doing some basic tests, it seems that while we can use _.mixin to create mixins as per the norm, we cannot reference them without getting a compilation error. Of course, we could put these references in the definition file, but I usually prefer not to modify that.

Is there any way to do what we're looking for, or are we out of luck?


回答1:


You can do this.

// somewhere in your project
declare module _ {
    interface LoDashStatic {
        foo(value: string): number;
    }
}

// extend it somewhere else 
declare module _ {
    interface LoDashStatic {
        bar(value: number);
    }
}

Test it out




回答2:


See TypeScript docs on extending built-in types, which I think applies here as well. _ is defined as var _: _.LoDashStatic, and vars are not currently extendable.

The best way that I found to expose extensions is via a lodash-mixins.ts script that defines a new LoDashMixins interface (extending LoDashStatic), applies the mixins, and exports _ cast to the custom interface. This example defines a single mixin, but the idea is to add all your mixins to one script for easy importing.

import * as _ from 'lodash';
import xdiff from './xdiff';

interface LoDashMixins extends _.LoDashStatic {
  xdiff<T>(array:T[], values:T[]): T[];
}

_.mixin({xdiff:xdiff});

export default <LoDashMixins>_;

When you want to use the mixins, import './lodash-mixins' instead of 'lodash'. You now have compile-time visibility to all of the built-in functions, as well as your mixins.

import _ from './lodash-mixins';

_.map([]); // built-in function still compiles
_.xdiff([], []); // mixin function compiles too



回答3:


You can do this using type erasure:

import _ = require('lodash');

_.mixin(require('lodash-deep'));

function deepSet(lodash: any, path: Array<string>, record: IFooRecord, 
        replacement: number): void { 
    lodash.deepSet(object, path, replacement); 
}

interface IBarRecord {
   bar: number;
}

interface IFooRecord {
   foo: IBarRecord;
}

var subject: IFooRecord = { 
   foo: {
      bar: 0
   }
};
var replacement: number = 1;

deepSet(_, ['foo', 'bar'], subject, replacement);

It's kind of a cludge, but your code will compile. You could also create your own proxy implementing the interface of the mixin and inject the lodash module instance into it to a achieve a more modular result:

import _ = require('lodash');

_.mixin(require('lodash-deep'));    

module 'lodash-deep' {

   export class lodashDeep {

     private _: any;

      constructor(lodash?: any) {
         if (!lodash) {
            lodash = _;
         }
         this._ = lodash;
      }

      public deepSet(collection: any, path: any, value: any): void {
         this._.deepSet(collection, path, value);
      }

      ...

   }

}



回答4:


For now, it looks like what I want isn't available without any pain. Instead, I have to modify the lodash.d.ts file to include the definitions that I want, similar to the following:

declare module _ {
    // Default methods declared here...

    //*************************************************************************
    // START OF MIXINS, THESE ARE NOT PART OF LODASH ITSELF BUT CREATED BY US!
    //*************************************************************************

    interface LoDashStatic {
        isNonEmptyString: (str: string) => boolean;
        isEmptyString: (str: string) => boolean;
        isEmptyArray: (a: any[]) => boolean;
        isNonEmptyArray: (a: any[]) => boolean;
        isNullOrEmptyString: (str: string) => boolean;
        isNullOrUndefined: (val: any) => boolean;
        isNullOrEmpty(value: any[]): boolean;
        isNullOrEmpty(value: _.Dictionary<any>): boolean;
        isNullOrEmpty(value: string): boolean;
        isNullOrEmpty(value: any): boolean;
    }

    //*************************************************************************
    // END OF MIXINS
    //*************************************************************************

    // Default types declared here...
}

I hate modifying the default files, but it seemed the lesser of the evils.




回答5:


I found the docs on module augmentation to be helpful. I used a combination of this and another answer.

// my-lodash.ts
import * as _ from 'lodash';

declare module 'lodash' {
  interface LoDashStatic {
    isNonEmptyString(str: string): boolean;
    isEmptyString(str: string): boolean;
    isEmptyArray<T>(a: T[]): boolean;
    isNonEmptyArray<T>(a: T[]): boolean;
    isNullOrEmptyString(str: string): boolean;
    isNullOrUndefined<T>(val: T): boolean;
    isNullOrEmpty<T>(value: T[]): boolean;
    isNullOrEmpty<T>(value: Dictionary<T>): boolean;
    isNullOrEmpty<T>(value: T): boolean;
  }
}

module LoDash {
  export function isEmptyArray<T>(a: T): boolean {
    return Array.isArray(a) && !a.length;
  }
  // the rest of your functions
}

_.mixin(Object.keys(LoDash)
               .reduce(
                 (object, key) => {
                   object[key] = LoDash[key];
                   return object;
                 },
                 Object.create(null)
              )); 

export = _;

Doing it this way, you can avoid casting or using a default export, which means you can continue importing in the same fashion.

Now, in some other file, utilize your augmented module as such:

// another-file.ts
import * as _ from './my-lodash';

_.isEmptyArray([]);
=> true


来源:https://stackoverflow.com/questions/28725869/how-to-use-lodash-mixin-in-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!