如何在 TypeScript 中使用 lodash.mixin

How to use lodash.mixin in TypeScript

我的团队正在评估将我们的一些文件从 JavaScript 转换为 TypeScript,并且我们在代码中广泛使用了一些自定义混合方法。通过一些基本测试,我们似乎可以使用 _.mixin 按照规范创建 mixins,但我们无法在不出现编译错误的情况下引用它们。当然,我们可以将这些引用放在定义文件中,但我通常不希望修改它。

有什么方法可以实现我们正在寻找的东西,还是我们运气不好?

您可以使用类型擦除来执行此操作:

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);

这有点麻烦,但您的代码可以编译。您还可以创建自己的代理来实现 mixin 的接口,并将 lodash 模块实例注入其中以获得更加模块化的结果:

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);
      }

      ...

   }

}

目前看来,我想要的东西似乎没有任何痛苦就无法获得。相反,我必须修改 lodash.d.ts 文件以包含我想要的定义,类似于以下内容:

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

我讨厌修改默认文件,但这似乎是害处较小。

你可以做到这一点。

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

请参阅 extending built-in types 上的 TypeScript 文档,我认为这也适用于此。 _ 定义为 var _: _.LoDashStaticvar 当前不可扩展。

我发现公开扩展的最佳方式是通过 lodash-mixins.ts 脚本定义新的 LoDashMixins 接口(扩展 LoDashStatic),应用混合,并导出 _ 转换为自定义界面。此示例定义了一个 mixin,但其想法是将所有 mixin 添加到一个脚本中以便于导入。

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>_;

当你想使用 mixins 时,导入 './lodash-mixins' 而不是 'lodash'。您现在 compile-time 可以查看所有 built-in 函数以及您的混入。

import _ from './lodash-mixins';

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

我发现 module augmentation 上的文档很有帮助。我结合使用了这个和另一个答案。

// 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 = _;

这样做,您可以避免转换或使用默认导出,这意味着您可以继续以相同的方式导入。

现在,在其他一些文件中,这样使用您的扩充模块:

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

_.isEmptyArray([]);
=> true