TypeScript: дискриминируемые объединения с необязательными значениями

даны следующие типы:

interface FullName {
  fullName?: string
}

interface Name {
  firstName: string
  lastName: string
}

type Person = FullName | Name;

const p1: Person = {};
const p2: Person = { fullName: 'test' };
const p3: Person = { firstName: 'test' }; // Does not throw
const p4: Person = { badProp: true }; // Does throw, as badProp is not on FullName | Name;

Я бы ожидал p3 приведет к ошибке компилятора, как firstName присутствует без lastName, но это не ... это ошибка или ожидается?

кроме того, создание FullName.fullName необходимые результаты в p3p1) вызывает ошибки.

2 ответов


тип в вашем вопросе не является, в обычном смысле, дискриминируемым Союзом - ваши члены Союза не имеют общего, необязательного буквального свойства дискриминантный.

Итак, как отметил @Alex в своем ответе, ваш союз несколько похож на

type Person = {
  fullName?: string
  firstName?: string
  lastName?: string
}

таким образом, он может быть инициализирован с { firstName: 'test' }

с истинными дискриминируемыми объединениями вы получаете логику для проверки необязательных свойств, а также проверки того, что литерал объекта может укажите только известные свойства:

interface FullName {
  kind: 'fullname';  
  fullName?: string
}

interface Name {
  kind: 'name';  
  firstName: string
  lastName: string
}

type Person = FullName | Name;

const p1: Person = {kind: 'fullname'};  // ok
const p2: Person = {kind: 'fullname', fullName: 'test' };  // ok

проверка необязательного свойства:

const p3: Person = {kind: 'name', firstName: 'test' }; 

ошибка:

Type '{ kind: "name"; firstName: string; }' is not assignable to type 'Person'.
  Type '{ kind: "name"; firstName: string; }' is not assignable to type 'Name'.
    Property 'lastName' is missing in type '{ kind: "name"; firstName: string; }'.

проверка дополнительного свойства:

const p5: Person = { kind: 'fullname', bar: 42 }

ошибка:

Type '{ kind: "fullname"; bar: number; }' is not assignable to type 'Person'.
  Object literal may only specify known properties, and 'bar' does not exist in type 'Person'.

, как выяснил @JeffMercado, проверка типа все еще немного выключена:

const p6: Person = { kind: 'fullname', firstName: 42 };  // no error. why?

Я бы рассмотрел возможность публикации проблемы для проекта GitHub typescript.


во-первых, ваш интерфейс FullName содержит только одно необязательное свойство, которое в основном делает его соответствующим чему-либо. Затем, когда вы делаете тип объединения с ним, результирующий тип будет совместим со всем.

, есть еще одна проблема с объявлением и назначением литеральных объектов, и это то, что вы можете только объявить известные свойства: почему я получаю ошибку " литерал объекта может указывать только известные свойства"?

таким образом, вы можете сделать это без каких-либо проблем:

var test = { otherStuff: 23 };
const p4: Person = test;

но не это

const p4: Person = { otherStuff: 23 };

а в твоем случае firstName является известным свойством FullName | Name, так что все ок.

и как @artem ответил,discriminated unions имеют особое значение в typescript, помимо регулярных союзов, требующих специальных структурных предположений.