Непредвиденная ошибка типа при использовании пересечений типов Typescript с различимыми объединениями

#typescript

#typescript

Вопрос:

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

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

Идея заключается в:

Объект должен содержать два логических значения — discriminant_1 и discriminant_2 . Если любое из этих дискриминантных полей является true , объект должен содержать дополнительное поле — extra_field_1 и extra_field_2 соответственно.

Итак, вот несколько примеров допустимых объектов:

 // Neither field is true - no extra fields
{ discriminant_1: false, discriminant_2: false }

// discriminant_1 is true - extra_field_1 present
{ discriminant_1: true, extra_field_1: true, discriminant_2: false }

// both discriminant fields are true - both extra fields present
{ discriminant_1: true, extra_field_1: true, discriminant_2: true, extra_field_2: true }
  

И недопустимый объект, потому discriminant_2 что есть true , но extra_field_2 отсутствует

 { discriminant_1: false, discriminant_2: true }
  

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

Для следующего объекта:

 { discriminant_1: false, discriminant_2: true }
  

Тип не компилируется, потому что, если discriminant_2 есть true , extra_field_2 должен быть указан в объекте. Но ошибка типа:

 Type '{ discriminant_1: false; discriminant_2: true; }' is not assignable to type 'FullType'.
  Type '{ discriminant_1: false; discriminant_2: true; }' is not assignable to type 'Discriminant1_True amp; Discriminant2_True'.
    Property 'extra_field_1' is missing in type '{ discriminant_1: false; discriminant_2: true; }' but required in type 'Discriminant1_True'.
  

Сообщение об ошибке подразумевает, что выводимый тип является Discriminant1_True amp; Discriminant2_True , несмотря на тот факт, что discriminant_1 есть false , и говорит, что extra_field_1 отсутствует, хотя на самом деле это то extra_field_2 , что отсутствует.

Ожидаемое сообщение об ошибке:

 Type '{ discriminant_1: false; discriminant_2: true; }' is not assignable to type 'FullType'.
  Type '{ discriminant_1: false; discriminant_2: true; }' is not assignable to type 'Discriminant1_False amp; Discriminant2_True'.
    Property 'extra_field_2' is missing in type '{ discriminant_1: false; discriminant_2: true; }' but required in type 'Discriminant2_True'.
  

Что укажет программисту на правильное отсутствующее поле.

Возможно ли записать типы таким образом, чтобы сообщения об ошибках, которые возвращаются при построении ошибочных типов, указывали на правильное отсутствующее поле?

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

1. Вам следует поискать аналогичную проблему на GitHub, я не думаю, что SO — подходящее место для этого вопроса. Поведение действительно такое, как вы описываете. Если вы добавите правильные поля, это сработает, просто сообщение об ошибке не работает. Я предполагаю, что, поскольку существует несколько возможных дискриминантов, typescript просто выбирает первый член объединения и генерирует ошибки для этого, даже если объект может быть ближе к другому типу в объединении.

2. Спасибо, Тициан! Я просмотрел Github, но не смог найти связанную проблему — однако кто-то указал мне на эту проблему , которая, насколько я могу судить, описывает ту же проблему

3. да, это похоже на точное. Я был уверен, что была аналогичная проблема :). Это в отставании, поэтому в какой-то момент оно может быть исправлено, хотя я подозреваю, что оно не имеет высокого приоритета

Ответ №1:

Позвольте мне дать свои 2 цента, и, надеюсь, кто-то намного мудрее меня (coff @basarat ) сможет рассказать вам еще больше интересных вещей 🙂

Я не уверен, что различимые объединения вполне дают вам то, что вам нужно. Могут быть определены пользователем средства защиты типов. Позвольте мне объяснить. Ваш объект примерно такой:

 interface NotTheTypeSafetyYouHopedFor { 
    discriminant_1: boolean; 
    extra_field_1?: boolean; 
    discriminant_2: boolean; 
    extra_field_2?: boolean; 
}
  

Но вы хотели бы немного усилить типы. Итак, давайте упростим проблему, забыв, что discriminant_2 она и extra_field_2? существует. Пуф! Они исчезли.

 interface False1 { 
    discriminant_1: false; 
}

interface True1 { 
    discriminant_1: true; 
    extra_field_1: boolean; 
}

type False1OrTrue1 = False1 | True1;

const exampleFalse1: False1 = { discriminant_1: false };
const exampleTrue1: True1 = { discriminant_1: true, extra_field_1: false };


function isTrue1(aFalse1OrTrue1: False1OrTrue1): aFalse1OrTrue1 is True1 { 
    return aFalse1OrTrue1.hasOwnProperty('extra_field_1')
}


function doSomething(aFalse1OrTrue1: False1OrTrue1) { 
    if (isTrue1(aFalse1OrTrue1)) {
        console.log(aFalse1OrTrue1.extra_field_1); // succeeds as this is a True1 
    } else { 
        console.log(aFalse1OrTrue1.extra_field_1); // compile error as this is a False1 
    }
}
  

Я подозреваю, что это подход, который может не масштабироваться напрямую, учитывая характер вашего вопроса (т.Е. discriminant_2 и т. Д.)

Но если бы каждое свойство в объекте было a False1OrTrue1 , то, возможно, оно соответствовало бы вашим потребностям? Надеюсь, это поможет. Guida говорит «привет» 🙂

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

1. Проблема не в функциональности типа. Тип функционирует так, как ожидает OP, он позволяет объектам OP считать допустимый проход, а ошибки в объектах OP считает недопустимыми (по крайней мере, в моем чтении, OP не стесняйтесь поправлять меня в этом). Единственная проблема заключается в том, что сообщение об ошибке для некоторых недопустимых объектов кажется запутанным. Ошибка должна быть там, проблема заключается только в сообщении

2. Спасибо, Джон! Я не часто использовал определяемые пользователем средства защиты типов, поэтому было бы здорово увидеть их вариант использования. К сожалению, я не думаю, что это сработает в моем случае. Мой фактический код имеет три дискриминанта объединения строк, каждый со своим собственным набором значений, и каждый из которых определяет различный набор полей в конечном объекте. Код действительно работает, т. Е. Он компилируется / не компилируется, когда объект является допустимым / недопустимым. Просто сообщения об ошибках в недопустимом случае не кажутся правильными. Кто-то указал мне на эту проблему , которая выглядит связанной

3. Рад попытаться помочь! Похоже, что проблема с GitHub очень актуальна. Приятно видеть, что вы пытаетесь ввести Type-Fu; системы ввода вызывают привыкание 😊