Type Guard Typescript неправильно определяет параметры остальных функций

У меня есть пример Typescript с параметрами type guard и function rest:

interface BaseProps {
  username: string;
  password: string;
}

type Props = BaseProps & (
  | {isAdmin: true, adminName: string} 
  | {isAdmin: false}
)


// Doesn't works
const myFn = ({isAdmin, ...rest}: Props) => {
  if(isAdmin === true) {
    rest.adminName; // Property 'adminName' does not exist on type '{ username: string; adminName: string; } | { username: string; }'.
  }
}

// It works
const myFn2 = (args: Props) => {
  if(args.isAdmin === true) {
    args.adminName;
  } 
}

Что не так с остальными параметрами и защитой типа?

TS Playground


ОБНОВИТЬ РЕШЕНИЕ:

Я нашел решение, используя Assert Functions для решения проблемы.

declare function assertType<T>(val: unknown): asserts val is T;

const myFn = ({password, isAdmin, ...rest}: Props) => {

  if(isAdmin === true) {
    
    assertType<Omit<Props & {isAdmin: true}, keyof Props>>(rest);

    rest.adminName; // <=== HERE

    rest.username;

    // Should be error
    rest.password;
  }
}

TS Playground


person nghiepit    schedule 27.10.2020    source источник


Ответы (1)


Тип Props:

{ имя пользователя: строка, isAdmin: правда, adminName: строка } | {имя пользователя: строка, isAdmin: ложь}

Когда вы устанавливаете значение isAdmin для объекта типа Prop (например, оператора if), машинописный текст может сузить фактический тип между { username: string, isAdmin: true, adminName: string } и { username: string, isAdmin: false }.

Если вы удалите isAdmin путем деструктурирования, тип rest станет

{ имя пользователя: строка, имя администратора: строка } | {имя пользователя: строка}

Значения isAdmin и rest становятся некоррелированными, и единственный способ сузить тип rest — подтвердить наличие свойства adminName.

Возможно, вам не следует использовать остальные параметры для этого конкретного случая. Вы также можете вручную преобразовать переменную rest в файл if.

person known-as-bmf    schedule 27.10.2020