Несколько вещей приводят к тому, что это происходит. Основная проблема здесь заключается в том, что TypeScript не поддерживает вложенные разграниченные объединения. Есть довольно старое открытое предложение на microsoft / TypeScript # 18758 ; если вам это небезразлично, вы можете указать go и указать ? или описать свой вариант использования, если он особенно убедителен. Однако сейчас это не часть языка. Это означает, что код, подобный этому, успешно выполняется:
type Discrim = { a: 0, c: string } | { a: 1, c: number };
declare const d: Discrim;
d.a === 0 ? d.c.toUpperCase() : d.c.toFixed(); // okay
, но код, подобный этому, не выполняется:
type NestedDiscrim = { a: { b: 0 }, c: string } | { a: { b: 1 }, c: number };
declare const n: NestedDiscrim;
n.a.b === 0 ? n.c.toUpperCase() : n.c.toFixed(); // error!
, потому что в первом случае Discrim
рассматривается компилятором как дискриминационное объединение с свойство a
как дискриминант, но в последнем случае NestedDiscrim
это , а не рассматривается как дискриминируемый союз со свойством a
в качестве дискриминанта.
Аналогично, в вашем case, Kind
является дискриминационным объединением, а ExCThingy | CThingy
- нет.
Для типов, которые компилятор видит как распознаваемое объединение, начиная с TypeScript 3.5, добавлена поддержка для назначений, подобных следующему:
const k: Kind = { kind: Math.random() < 0.5 ? "A" : Math.random() < 0.5 ? "B" : "C" }; // okay
Объект, присвоенный k
, имеет тип {kind: "A" | "B" | "C"}
, который технически не может быть назначен ни одному отдельному члену различимого объединения Kind
Это была ошибка, которую вы получили в TS3.4 и ниже:
// Type '{ kind: "A" | "B" | "C"; }' is not assignable to type 'Kind'.
Но в TS3.5 и выше компилятор выполняет дополнительную проверку, чтобы взять один тип объекта со свойством дискриминанта типа объединения и распространить союз upwa в объединение типов объектов с однотипными дискриминантными свойствами. И так это компилируется.
К сожалению, как мы упоминали, ExCThingy | CThingy
не является дискриминационным объединением в соответствии с компилятором. И вышеупомянутая поддержка распространяется только на дискриминационные союзы. Для недискриминационных объединений вы получаете ту же ошибку:
type NotDiscrim = { a: string } | { a: number };
const x: NotDiscrim = { a: Math.random() < 0.5 ? "" : 1 }; // error in all versions of TS
// Type '{ a: string | number; }' is not assignable to type 'NotDiscrim'
Компилятор просто не выполняет анализ распространения объединения на недискриминационных объединениях. А поскольку ExCThingy | CThingy
не считается дискриминационным объединением, значение типа { foo: Kind, something: 'test', somethingelse: 'test2' }
не считается типом ExCThingy | CThingy
.
Так вот что происходит. Чтобы продолжить здесь, вы можете использовать утверждение типа , чтобы сообщить компилятору, что вы уверены, что то, что вы делаете, безопасно:
fooArray.map(e => DoSomething({
foo: e.foo,
something: 'test',
somethingelse: 'test2'
} as ExCThingy | CThingy)); // no error
Либо так, либо вы разбили один объект в объединение, которое на самом деле может проверить компилятор, например:
fooArray.map(e => DoSomething(e.foo.kind === "C" ?
{ foo: e.foo, somethingelse: 'test2' } : { foo: e.foo, something: 'test' }
));
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код