Javascript / Typescript .bind () получить переменную в связанной функции - PullRequest
0 голосов
/ 21 октября 2019

Я хочу связать переменную с функцией обратного вызова, но mongoose уже встраивает параметр с именем val в эту функцию. Когда я хочу привязать другое значение с помощью .bind () к функции обратного вызова, я теряю исходный атрибут val. Есть ли способ сохранить исходный параметр и добавить к нему свою переменную с помощью .bind ()? Ниже приведен пример.

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

Что я хочу сделать:

// 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'
  },
]
...

Так что я могу установить максимальную длину в объекте валидатора.

1 Ответ

1 голос
/ 21 октября 2019

Curry

Вы можете изменить свою функцию, поэтому вместо двух аргументов она принимает один, а затем возвращает вторую функцию, которая принимает второй аргумент. Этот известный метод известен как curry .

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

// ... later ...

  validator: maxLengthValidator(255);

Таким образом, вы можете создавать различные валидаторы, вызывая функцию: maxLengthValidator(10) даст вам валидатор для 10 символов, которые вы можете использовать,Поскольку это функция, вы также можете просто присвоить ее переменной:

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

Параметры подкачки для обеспечения частичного распределения

Вместо принятия значения , а затем максимальная длина, возьми длину в первую очередь. То же самое, что и при каррировании функции, но вместо этого этот может принимать два параметра:

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

// ... later ...

  validator: maxLengthValidator.bind(this, 255);

Та же основная идея. На этот раз у вас немного больше гибкости - вы можете вызвать функцию с обоими параметрами maxLengthValidator(10, username) или просто частично применить ее с одним. Последний производит почти то же самое, что и карри, поскольку вы все еще получаете новую функцию с той же сигнатурой:

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

Несмотря на сходство карри не является частичным применением . Кроме того, можно использовать функцию любой арности (любое количество параметров), чтобы можно было принимать один или несколько одновременно. Например, вы можете увидеть _.curry в 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>

Частично подать заявку с заполнителем или справа

Вы можете оставить подпись как есть и просто изменить частичное заявление. Function#bind не очень гибок, но вы можете написать свой собственный или (еще лучше) использовать библиотеку. Я снова буду использовать Lodash, так как он имеет хорошую реализацию для них:

Частичное приложение с заполнителем

Позволяет пропускать некоторых параметров при частичном применении,Таким образом, вы можете пропустить первый и просто установить второй:

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>

Частично применяется справа

Частично применяется начиная справа налево, поэтому сначала будет установлено 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>
...