简体   繁体   中英

Javascript/ Typescript .bind() get Variable in bound function

i want to bind a variable to a callback function, but mongoose already embeds a parameter named val into that function. When i want to bind another value with.bind() to the callback function I lose the original val attribute. Is there a way i can keep the original parameter and add my variable with.bind() to it? Below is an example.

// gets val parameter from validator
const maxLengthValidator = (val: string): boolean => {
  if (val.length > 255) return false;
  return true;
};

const userSchema = new Schema({
  email: {
    type: String,
    required: true,
    // unique: true
    validate: [
      {
        validator: maxLengthValidator, // includes a val attribute from post request
        msg: 'too short'
      },
    ]
  },
  password: {
    type: String,
    required: true
  }
});

export default model('User', userSchema);

What i want to do:

// gets val parameter from validator
const maxLengthValidator = (val: string, boundary: number): boolean => {
  if (val.length > boundary) return false;
  return true;
};

...
validate: [
  {
    validator: maxLengthValidator.bind(this, 255), // doesn't work, would need to specify the string for the val parameter
    msg: 'too short'
  },
]
...

So i can set a max length in the validator object.

Curry

You can change your function, so instead of taking two argument, it takes one, then returns a second function that takes the second argument. This is known technique is known as curry .

// maxLengthValidator :: number -> string -> boolean 
const maxLengthValidator = (boundary: number) => (val: string) : boolean  => {
  if (val.length > boundary) return false;
  return true;
};

// ... later ...

  validator: maxLengthValidator(255);

This way you can create different validators by invoking the function: maxLengthValidator(10) will give you a validator for 10 characters you can use. Since it's a function, you can just assign it to a variable, too:

//userNameLengthValidator :: string -> boolean
const userNameLengthValidator: (val: string) => boolean = maxLengthValidator(10);

Swap parameters to accommodate partial allocation

Instead of taking value then max length, take the length first. Same thing that currying the function did but instead this one can still take two parameters:

const maxLengthValidator = (boundary: number, val: string): boolean => {
  if (val.length > boundary) return false;
  return true;
};

// ... later ...

  validator: maxLengthValidator.bind(this, 255);

Same basic idea. This time you have a bit more flexibility, though - you can call the function with both parameters maxLengthValidator(10, username) or just partially apply it with one. The latter produces pretty much the same thing as currying, since you still get a new function with the same signature:

//userNameLengthValidator :: string -> boolean
const userNameLengthValidator: (val: string) => boolean = maxLengthValidator.bind(this, 10);

Despite the similarities currying is not partial application . Also, it's possible to curry a function of any arity (any amount of parameters) to be able to take either one or many at a time. For an example, you can see _.curry in Lodash

 function add4(a, b, c, d) { console.log(a + b + c + d); } const curryAdd4 = _.curry(add4); curryAdd4(1)(2)(3)(4); curryAdd4(1, 2, 3, 4); curryAdd4(1)(2, 3)(4); curryAdd4(1, 2, 3)(4); curryAdd4(1)(2, 3, 4);
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

Partially apply with a placeholder or from the right

You can leave the signature as is, and just change your partial application. Function#bind is not very flexible but you can write your own or (better yet) use a library. I'll use Lodash again, since it has a good implementation for these:

Partial application with a placeholder

Allows you to skip some of the parameters when partially applying. So you can skip the first one and just set the second:

 const maxLengthValidator = (val, boundary) => { if (val.length > boundary) return false; return true; }; //userNameLengthValidator:: string -> boolean const usernameLengthValidator = _.partial(maxLengthValidator, _, 10); console.log(usernameLengthValidator("usernameTooLong")); console.log(usernameLengthValidator("user"));
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

Partially apply from the right

Partially apply starting from right to left, so it will first set boundary :

 const maxLengthValidator = (val, boundary) => { if (val.length > boundary) return false; return true; }; //userNameLengthValidator:: string -> boolean const usernameLengthValidator = _.partialRight(maxLengthValidator, 10); console.log(usernameLengthValidator("usernameTooLong")); console.log(usernameLengthValidator("user"));
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM