Машинопись Дискриминационный Союз допускает недопустимое состояние - PullRequest
0 голосов
/ 06 октября 2018

Я пытаюсь использовать Typescript Discripted Union для моделирования довольно распространенного сценария при асинхронной загрузке данных:

type LoadingState = { isLoading: true; }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState =   { isLoading: false; isSuccess: false; errorMessage: string; }

type State = LoadingState | SuccessState | ErrorState;

Насколько я понимаю, это должно ограничивать допустимые комбинациизначения в соответствии с определениями типа.Однако система типов с радостью примет следующую комбинацию:

const testState: State = {
    isLoading: true,
    isSuccess: true,
    errorMessage: "Error!"
}

Я ожидаю здесь ошибку.Я что-то упускаю или каким-то образом неправильно использую определения типов?

1 Ответ

0 голосов
/ 06 октября 2018

Эта проблема связана с тем, как проверки избыточных свойств работают в профсоюзах.Если литерал объекта назначен переменной типа объединения, свойство не будет помечено как избыточное, если оно присутствует в любом членов объединения.Если мы не считаем избыточные свойства ошибкой (и, за исключением литералов объекта, они не считаются ошибкой), указанным литералом объекта может быть экземпляр LoadingState (экземпляр с isLoading, установленным на true как указано и несколько лишних свойств).

Чтобы обойти это нежелательное поведение, мы можем добавить свойства к LoadingState, чтобы сделать ваш объект буквально несовместимым с LoadingState

type LoadingState = { isLoading: true; isSuccess?: never }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState =   { isLoading: false; isSuccess: false; errorMessage: string; }

type State = LoadingState | SuccessState | ErrorState;

const testState: State = { // error
    isLoading: true,
    isSuccess: true,
    errorMessage: "Error!"
}

Мы могли бы даже создать тип, который бы гарантировал добавление такого члена

type LoadingState = { isLoading: true; }
type SuccessState = { isLoading: false; isSuccess: true; }
type ErrorState =   { isLoading: false; isSuccess: false; errorMessage: string; }

type UnionKeys<T> = T extends any ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>

type State = StrictUnion< LoadingState | SuccessState | ErrorState>

const testState: State = { // error
    isLoading: true,
    isSuccess: true,
    errorMessage: "Error!"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...