Typescript не рассматривает «случай» оператора switch как средство защиты от ввода?

#typescript

#typescript

Вопрос:

У меня есть приведенный ниже (значительно упрощенный для целей этого примера) код.

 type Todo = {
    id: string;
    text: string;
};

type Action =
  | { type: 'DELETE'; payload: string }
  | { type: 'CREATE'; payload: Todo }

  function reducer(state: Todo[], { type, payload }: Action) {
        switch (type) {
            case 'CREATE':
                const trimmedText = payload.text.trim(); // TS Error - Property 'text' does not exist on type 'string'
                return [...state, payload];
            case 'DELETE':
                return state.filter((todo) => todo.id !== payload); // TS still thinks, at this line, that the payload could be either string or Todo
            default:
                throw new Error();
        }
    }


  

Вопросы

  • Почему typescript не распознает, что в случае «CREATE» полезная нагрузка не может быть строкой?

  • И в случае «УДАЛЕНИЯ» почему Typescript не распознает, что полезная нагрузка должна быть строкой?

Комментарии:

1. Typescript недостаточно умен, чтобы вводить 2 независимые переменные, это сработало бы, если бы вы не использовали деструктурирование.

2. @zerkms Ты легендарный приятель! Я потратил часы, пытаясь разобраться в этом, хех. Не могли бы вы указать это в ответе, чтобы я мог пометить его как принятое правильное решение для этого?

Ответ №1:

Насколько я помню, на данный момент это ограничение typescript: он пока не может сузить типы для 2 «независимых» переменных. На данный момент я не могу найти соответствующую проблему github, но я думаю, что видел ее.

Тем не менее, чтобы заставить его работать, вместо этого работайте напрямую с action: Action переменной и не разрушайте ее.

 type Action =
  | { type: 'DELETE'; payload: string }
  | { type: 'CREATE'; payload: Todo }

  function reducer(state: Todo[], action: Action) {
        switch (action.type) {
            case 'CREATE':
                // ...
            case 'DELETE':
                // ...
            default:
                throw new Error();
        }
    }