Typeccript пользовательский объект проверки типа защиты имеет все свойства в массиве - PullRequest
2 голосов
/ 23 января 2020

Я пытаюсь написать определяемый пользователем тип защиты , который проверяет, имеет ли данное значение все свойства в данном массиве.

Я вызываю эту функцию hasAll и его реализация и использование в Javascript будут выглядеть следующим образом:

function hasAll(obj, keysToCheck) {
  if (!obj) return false;

  for (const key of keysToCheck) {
    const value = obj[key];
    if (value === null) return false;
    if (value === undefined) return false;
  }

  return true;
}

hasAll({ foo: 'test', bar: 5 }, ['foo', 'bar']); // true

hasAll({ foo: 'test', bar: 5 }, ['foo', 'bar', 'baz']); // false

Теперь я пытаюсь превратить вышеуказанную функцию в охранник типа . Это то, что у меня есть:

// this _almost_ works ?
type Nullable<T> = T | null | undefined;

type RemoveNullables<T, K extends keyof T> = {
  [P in K]-?: T[P] extends Nullable<infer U> ? U : T[P];
};

function hasAll<T, K extends keyof NonNullable<T>>(
  obj: T,
  keysToCheck: K[],
): obj is RemoveNullables<NonNullable<T>, K> {
  // but i'm getting an error here ???
  if (!obj) return false;

  const nonNullableObj = obj as NonNullable<T>;

  for (const key of keysToCheck) {
    const value = nonNullableObj[key];
    if (value === null) return false;
    if (value === undefined) return false;
  }

  return true;
}

export default hasAll;

ссылка на игровую площадку


Сообщение об ошибке:

A type predicate's type must be assignable to its parameter's type.
  Type 'RemoveNullables<NonNullable<T>, K>' is not assignable to type 'T'.

I ' я прочитал этот ответ с хорошим объяснением, однако это не очень помогает моему делу.

Я хочу явно заявить, что мой тип T будет соответствовать RemoveNullables<NonNullable<T>, K> после его запуска через эту функцию. Меня не волнует, можно ли присвоить T для RemoveNullables<NonNullable<T>, K> (если это имеет смысл).


  1. Я поступаю неправильно? Есть ли лучший способ написать этот тип защиты?
  2. Если этот подход хорош, как я могу сказать машинописи, что мне все равно, если защита типа сама по себе "небезопасна"?

1 Ответ

2 голосов
/ 23 января 2020

Кажется, это соответствует вашим требованиям:

type ExcludeNullable<T, K extends keyof NonNullable<T>> = NonNullable<T> & {
    [k in K]-?: Exclude<NonNullable<T>[k], null | undefined>
}

function hasAll<T, K extends keyof NonNullable<T>>(
    obj: T,
    keysToCheck: K[]
): obj is ExcludeNullable<T, K> {
    return obj !== null && obj !== undefined
        && keysToCheck.every(k => obj![k] !== null && obj![k] !== undefined);
}

Несколько замечаний:

  • Тип пересечения T & ... гарантирует, что ExcludeNullable<T, K> может быть назначено T. Без этого сопоставленный тип не имеет свойств T, отсутствующих в K.
  • Exclude - это более простой способ избавиться от null и undefined, чем при использовании условный тип с infer.
  • Я позволил себе немного упростить реализацию функции hasAll.

Playground Link

...