TypeScript: рекурсивный, глубокий, изменяемый с помощью Generics.Ошибка: T не может быть назначен Mutable <T> - PullRequest
0 голосов
/ 18 декабря 2018

Я пытаюсь написать глубоко рекурсивный тип Mutable:

Mutable<T> = ...
    // remove all "readonly" modifiers
    // convert all ReadonlyArray to Array
    // etc.
    // and do it all recursively

const mutate = <T>(val: T, mutateFn: (mutableVal: Mutable<T>) => void): T => {
  return null as any
}

Работает нормально, пока я не использую дженерики:

// works fine
mutate([] as ReadonlyArray<string>, mutableVal => {
  mutableVal.splice(42, 0, "test");
});

Но когдаиспользуя его в общей функции, я получаю сообщение об ошибке:

// Error: Argument of type 'T' is not assignable to parameter of type 'Mutable<T>'.
const doSomething = <T>(array: ReadonlyArray<T>, index: number, elem: T) => {
  mutate(array, mutableVal => {
                      // Here's the error
                      //         v
    mutableVal.splice(index, 0, elem);
  });
}

Я понимаю, что тип изменяемого массива - Array<Mutable<T>>, и что splice теперь ожидает значение Mutable<T> вместо T.Но я не могу понять, как ее решить.

У вас есть идеи, как решить эту проблему?

Я создал площадку TypeScript, поэтому вы можете играть с кодом: Ссылка на TypeScript Playground

1 Ответ

0 голосов
/ 18 декабря 2018

Я предлагаю сделать что-то вроде этого:

const doSomething = <T>(array: ReadonlyArray<T>, index: number, elem: T) => {
    mutate({ array: array, elem: elem }, mutableVal => {
        mutableVal.array.splice(index, 0, mutableVal.elem);
    });
}

Идея состоит в том, что вам нужно elem, чтобы быть изменяемым, чтобы добавить его в массив с глубокими изменениями, но ваш исходный вызов былне делаю этого.Так как вы хотите изменить как array, так и, возможно, elem, самое простое решение - передать объект, содержащий как array, так и elem, и работать с его глубоко изменяемой версией.

Только вы знаете, допустимо ли вызывать mutate() на elem, а также на array, так как реализация mutate() исключена.Я предполагаю, что это будет чем-то вроде утверждения:

const mutate = <T>(val: T, mutateFn: (mutableVal: Mutable<T>) => void): T => {
    mutateFn(val as Mutable<T>); //?‍♀️
    return val;
}

, и в этом случае я бы сказал «кого волнует», звоните ли вы mutate() на elem или простоУтвердите elem его изменяемой копии внутри doSomething().С другой стороны, если ваша реализация представляет собой нечто более изворотливое с использованием клонирования, вам следует подумать о том, имеет ли смысл вызывать ее на elem или нет.

Хорошо, надеюсь, это поможет.Удачи!

...