Typescript жалуется при передаче возможно нулевого (но не нулевого) значения анонимному обратному вызову - PullRequest
0 голосов
/ 16 февраля 2020

Когда у меня есть свойство, которое изначально начинается со значения NULL, как я могу передать его в качестве аргумента анонимной функции обратного вызова, которая ожидает ненулевое значение, без жалоб на машинопись?

Я могу обернуть свой вызов функции в операторе if для проверки на null, который останавливает жалобы на машинопись на уровне вызова функции, но в обратном вызове машинопись все еще жалуется.

Вот фрагмент кода. Я допускаю, чтобы некоторые DatabaseState свойства были нулевыми, так как они инициализируются.

//types.ts
export interface DatabaseState {
  db: PouchDB.Database | null;
  remoteDb: PouchDB.Database | null;
  dbReplication: PouchDB.Replication.Sync<{}> | null;
  dbSyncStatus: number;
  dbSyncInfo: string;
  dbSyncError: string;
}

//state typing - state: DatabaseState

//An action in database.ts
async startDatabaseSyncronisation({ state, commit, dispatch }) {
  var opts = { live: true, retry: true };

  if (state.remoteDb != null && state.db != null) {

    /* THE IF STATEMENT PREVENTS THE BELOW FUNCTION FROM CAUSING TYPESCRIPT TO COMPLAIN */

    await PouchDB.replicate(state.remoteDb, state.db, opts).on("complete",
      function(info) {

        /* WITHIN THE CALLBACK FUNCTION THOUGH, TYPESCRIPT COMPLAINS THAT state.remoteDb and state.db
        COULD BE NULL AND THE FUNCTION DOESN'T ACCEPT A NULL VALUE */

        let replication = PouchDB.sync(state.remoteDb, state.db, opts)
          .on("paused", function(err: {}) {
            dispatch("onSyncPaused", err);
          })
          .on("active", function() {
            dispatch("onSyncActive");
          })
          .on("denied", function(err: {}) {
            dispatch("onSyncError", err);
          })
          .on("error", function(err: {}) {
            dispatch("onSyncError", err);
          })
          .on("complete", function() {});
        commit(SET_DB_REPLICATION, replication);
      }
    );
  }
},

Как мне подходить к такого рода проверкам печати? Есть ли более «дружественный к Typescript» способ описания начального значения как unset?

В проекте используется Vue, с Vuex для управления состоянием.

Ответы [ 3 ]

0 голосов
/ 16 февраля 2020

Я бы очень рекомендовал вам всегда учесть null. Концепция null была сочтена ошибкой, и отсутствие защиты от этой ценности делает ваш код небезопасным. Но если вы настаиваете, есть вариант, который вы можете отключить tsconfig.json с именем strictNullChecks https://www.typescriptlang.org/docs/handbook/compiler-options.html

Если вы даже попытаетесь выполнить запрос, если remoteDb или db is null`? Разве это не повредит ваш код во время выполнения?

РЕДАКТИРОВАТЬ: функция просто никогда не будет работать, если любое из значений равно нулю? Если это так, replicate и sync неправильно введены библиотекой. Надзор со стороны авторов пакета.

Нет "хорошего" способа объяснить фундаментальную ошибку третьей стороны. Самый простой способ документировать это, но все же продолжать печатать, это создать type с именем переменной для объявления причины. Может быть, у кого-то есть идея получше, но я бы сделал это:

type POUCH_BD_DATABASE_OR_NULL = PouchDB.Database

export interface DatabaseState {
  db: POUCH_BD_DATABASE_OR_NULL;
  remoteDb: POUCH_BD_DATABASE_OR_NULL;
  dbReplication: PouchDB.Replication.Sync<{}> | null;
  dbSyncStatus: number;
  dbSyncInfo: string;
  dbSyncError: string;
}

EDIT2: Если вы выполняете проверки на state до , оно передается в startDatabaseSyncronisation, затем ваш ввод, который вы используете для startDatabaseSyncronisation, в корне неверен, и вы не можете напрямую делиться interface DatabaseState между двумя функциями. Лучшее, что вы можете сделать, - это создать interface общих элементов в обоих, а затем extend в двух интерфейсах.

interface DatabaseMetaData {
    dbSyncStatus: number;
    dbSyncInfo: string;
    dbSyncError: string;
}

// for the function where it might be null
export interface DatabaseState extends DatabaseMetaData{
    db: PouchDB.Database | null;
    remoteDb: PouchDB.Database | null;
    dbReplication: PouchDB.Replication.Sync<{}> | null;
}

// for startDatabaseSyncronisation
export interface DatabaseSyncState extends DatabaseMetaData{
    db: PouchDB.Database;
    remoteDb: PouchDB.Database;
    dbReplication: PouchDB.Replication.Sync<{}>;
}
0 голосов
/ 16 февраля 2020

Ответы @elderapo и @Andrew были полезны.

Ответ @ elderapo наиболее прямо ответил на мой первоначальный запрос: «почему машинопись не жалуется на вызов функции, а жалуется на обратный вызов? ":

Значение состояния может измениться где-то между обратным вызовом if check и complate

И @Andrew обратил внимание на основную проблему, используя null в качестве допустимого значение в этом случае, вероятно, небезопасно.

Я настоятельно рекомендую вам всегда учитывать нулевое значение. Концепция null была сочтена ошибкой, и отсутствие защиты от этого значения делает ваш код небезопасным

Хотя я не ожидаю, что значения db и remoteDb вернутся к нулю между отправкой действия и обратным вызовом лучше вообще исключить эту возможность. Отсутствие соединения / потеря соединения с базой данных обрабатываются в другом коде.

Вместо того, чтобы разрешить типу быть null просто для инициализации, я теперь инициализирую их как пустые объекты с приведением типа. Поэтому мне не нужно отключать strictNullChecks (я хочу их).

Новый код:

export interface DatabaseState {
  db: PouchDB.Database;
  remoteDb: PouchDB.Database;
  dbReplication: PouchDB.Replication.Sync<{}>;
  dbSyncStatus: number;
  dbSyncInfo: string;
  dbSyncError: string;
}

const state: DatabaseState = {
  db: {} as PouchDB.Database,
  remoteDb: {} as PouchDB.Database,
  dbReplication: {} as PouchDB.Replication.Sync<{}>,
  dbSyncStatus: SyncStatus.NONE,
  dbSyncInfo: "",
  dbSyncError: ""
};


async startDatabaseSyncronisation({ state, commit, dispatch }) {
  var opts = { live: true, retry: true };
  await PouchDB.replicate(state.remoteDb, state.db, opts).on(
    "complete",
    function(info) {
      let replication = PouchDB.sync(state.remoteDb, state.db, opts)
        .on("paused", function(err: {}) {
          dispatch("onSyncPaused", err);
        })
        .on("active", function() {
          dispatch("onSyncActive");
        })
        .on("denied", function(err: {}) {
          dispatch("onSyncError", err);
        })
        .on("error", function(err: {}) {
          dispatch("onSyncError", err);
        })
        .on("complete", function() {});
      commit(SET_DB_REPLICATION, replication);
    }
  );
},
0 голосов
/ 16 февраля 2020

Значение состояния может измениться где-то между проверкой if и обратным вызовом complate. Если вы уверены, что состояние не изменилось, вы можете сказать компилятору, что эти значения не обнуляются, используя оператор нулевого подтверждения : state!.remoteDb.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...