我的团队正在评估将一些文件从 JavaScript 切换到 TypeScript,并且我们在代码中广泛使用了一些自定义 mixin 方法。从一些基本测试来看,虽然我们可以使用 _.mixin 按照规范创建 mixin,但我们无法在不出现编译错误的情况下引用它们。当然,我们可以将这些引用放在定义文件中,但我通常不喜欢修改它。
有什么办法可以实现我们正在寻找的东西,还是我们运气不好?
请参阅关于 扩展内置类型 的 TypeScript 文档,我认为这也适用于这里。
_
定义为 var _: _.LoDashStatic
,并且 var
目前不可扩展。
我发现公开扩展的最佳方法是通过
lodash-mixins.ts
脚本定义新的 LoDashMixins
接口(扩展 LoDashStatic
),应用 mixins,并将 _
转换导出到自定义接口。此示例定义了一个 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 _ as LoDashMixins;
当您想使用 mixins 时,请导入
'./lodash-mixins'
而不是 'lodash'
。现在,您可以在编译时查看所有内置函数以及 mixin。
import _ from './lodash-mixins';
_.map([]); // built-in function still compiles
_.xdiff([], []); // mixin function compiles too
你可以做到这一点。
// somewhere in your project
declare module _ {
interface LoDashStatic {
foo(value: string): number;
}
}
// extend it somewhere else
declare module _ {
interface LoDashStatic {
bar(value: number);
}
}
您可以使用类型擦除来做到这一点:
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...
}
我讨厌修改默认文件,但这似乎是危害较小的一个。
我发现关于模块增强的文档很有帮助。我结合使用了这个和另一个答案。
// 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
除了 @smartboy 的 answer 之外,您实际上不需要声明彼此分开的类型和实际函数。这就是您创建 mixin 并直接从中推断类型的方法:
// util/lodash.mixins.ts
import _ from 'lodash';
class LoDashMixins {
isStringEmpty(str: string) {
str.length === 0;
}
sum(a: number, b: number) {
return a * b;
}
yourFunction() {
return 'foo' as const;
}
}
/* -------------------------- */
// This one binds your mixin functions
_.mixin(
Object.getOwnPropertyNames(Mixins.prototype).reduce((obj, name) => {
obj[name] = Mixins.prototype[name];
return obj;
}, {}),
);
// And this one mixes in your types!
declare module 'lodash' {
interface LoDashStatic extends LoDashMixins {}
}
如上所示,您只需声明一次函数。在这种情况下,类是覆盖运行时实体和打字稿类型的好方法