Могут ли вложенные операторы switch быть исчерпывающими в TypeScript?

Похоже, следующий код не выполняет проверку типов в TypeScript.

interface A {
    type: 'a',
    number: Number
}

interface B {
    type: 'b',
    number: Number
}

type Letter = A | B

interface One {
    type: 'one'
}

interface Two {
    type: 'two'
}

type Number = One | Two;


function we(letter: Letter): boolean { // claims boolean isn't always returned
    switch (letter.type) {
        case 'a':
            return true;

        case 'b':
            const number = letter.number;
            switch (number.type) {
                case 'one':
                    return true;
                case 'two':
                    return true;
            }
    }
}

Я могу добавить значение по умолчанию для внутреннего переключателя, чтобы заставить его работать, но я удивлен, что мне нужно, учитывая, что плоский переключатель работает просто отлично. Что именно здесь происходит?


person Anthony Naddeo    schedule 05.01.2018    source источник


Ответы (1)


Проблема здесь в том, что вы не обработали случай, когда данные не соответствуют заявленным вами. Может где-то могли быть приведения к любому и тогда присваивание letter.type='c'. Или, если уж на то пошло, просто назовите это так:

we({type: 'c'} as any as Letter);

Двойное приведение гарантирует, что вы не получите предупреждений компилятора, но это не тот случай, который вы разрешили в функции.

Вот один из способов сортировки:

function exhaustiveCheck(value: never): never {
  throw(new Error('switch was not exhaustive'));
}

function we(letter: Letter): boolean {
    switch (letter.type) {
        case 'a':
            return true;

        case 'b':
            const number = letter.number;
            switch (number.type) {
                case 'one':
                    return true;
                // case 'two':
                //     return true;
                default:
                  return exhaustiveCheck(number);
            }
        default: {
          return exhaustiveCheck(letter);
        }
    }
}

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

Как я написал код выше, вы получаете ошибку времени компиляции для вызова exhaustiveCheck(number), раскомментируйте две строки над ней, и ошибка исчезнет. Второй вызов exhaustiveCheck(letter) принимается, потому что в случае по умолчанию мы уже рассмотрели оба типа A и B, поэтому остается единственный случай, когда letter имеет тип never: это означает, что с точки зрения системы типов это никогда не должно происходить. хотя во время выполнения это возможно могло произойти.

person Duncan    schedule 05.01.2018
comment
Мне трудно это понять. Разве эта логика не применима к не вложенному коммутатору, который работает нормально? Что особенного во вложенных коммутаторах? - person Anthony Naddeo; 06.01.2018