Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use mixins with lodash and typescript with chaining support

I'm having trouble using Lodash with typescript when I'm trying to extend lodash with my custom mixins.

My unsuccessful attempt:

Lets say I add a new function to lodash using mixins as follows:

//lodashMixins.ts

import * as _ from "lodash";

_.mixin({
  swap
});

function swap(array: Array<any>, index1: number, index2: number) {
    let temp = array[index1];
    array[index1] = array[index2];
    array[index2] = temp;
    return array;
}

The function swap is not available on _ if I import _ from "lodash"; in other files.

Partially successful attempt:

Then I looked for help, and people suggested to extend _.LoDashStatic and then export the _ as the new extended interface.

Then I did following:

//lodashMixins.ts

import * as _ from "lodash";

interface LodashExtended extends _.LoDashStatic {
  swap(array: Array<any>, index1: number, index2: number): Array<any>;
}

_.mixin({
  swap
});

function swap(array: Array<any>, index1: number, index2: number) {
    let temp = array[index1];
    array[index1] = array[index2];
    array[index2] = temp;
    return array;
}

export default _ as LodashExtended;

And using the new mixed-in as following:

//otherFile.ts

import _ from "./lodashMixins";

function someFn(){
    var array = [1,2,3,4]
    _.swap(array, 1, 2);
}

Now this works, but there are two issues:

  1. First that the new swap function is not working with lodash's chained syntax (neither with explicit chaining nor with implicit chaining).

which means, that typescript gets angry if I do any of the following:

//otherFile.ts

import _ from "./lodashMixins";

function someFn(){
    var cantDothis = _.chain([1,2,3,4]).map(x=>x*x).swap(1,2).value();
    //[ts] Property 'swap' does not exist on type 'LoDashExplicitWrapper<number[]>'.

    var neitherThis = _([1,2,3,4]).map(x=>x*x).swap(1,2).value();
    //[ts] Property 'swap' does not exist on type 'LoDashImplicitWrapper<number[]>'.
}
  1. Second that I have to do this ugly import _ from "./lodashMixins"; instead of standard import _ from "lodash";.

Someone please come up with an elegant solution that provides typescript type support while chaining without any code smell or ugliness.. Thanks. :)

May be John-David Dalton can help.

like image 568
pranavjindal999 Avatar asked Jan 02 '23 12:01

pranavjindal999


1 Answers

You are looking for module augmentation. You can extend interfaces in existing modules by redefining them and adding extra methods. In this case you should augment LoDashStatic for static usage and LoDashExplicitWrapper for chain usage. When you use the module, you can first import lodash and then import the module containing swap for its side effects (the side effect of adding the method to lodash)

// swap.ts
import * as _ from "lodash";


declare module "lodash" {
    interface LoDashStatic {
        swap<TValue>(array: Array<TValue>, index1: number, index2: number): TValue[];
    }
    interface LoDashExplicitWrapper<TValue> {
        swap(index1: number, index2: number): LoDashExplicitWrapper<TValue>;
    }
}

_.mixin({
    swap
});

function swap(array: Array<any>, index1: number, index2: number) {
    let temp = array[index1];
    array[index1] = array[index2];
    array[index2] = temp;
    return array;
}

//usage 
import * as _ from "lodash";
import "./swap"


console.log(_.chain([1,2,3,4]).map(x=>x*x).swap(1,2).map(x=> x * 2).value());

var array = [1,2,3,4]
console.log(  _.swap(array, 1, 2));

You can read more about module augmentation here

like image 92
Titian Cernicova-Dragomir Avatar answered Jan 05 '23 16:01

Titian Cernicova-Dragomir