Если вы планируете проверять значение типа объединения с помощью оператора switch/case
, вы, вероятно, должны сделать его исключенным объединением , где свойство type
каждого компонента объединения объявлено каксоответствующий строковый литерал вместо просто string
.Вам на самом деле не нужны условные типы, чтобы справиться с этим, по крайней мере, не внутри вашей someFunction()
реализации.
Например, скажем, ваши типы выглядят так:
type MyType1 = { type: "type1", data: { a: string, b: number } };
type MyType2 = { type: "type2", data: { c: boolean, d: string } };
type MyType3 = { type: "type3", data: { e: number, f: boolean } };
type MyComplexType = MyType1 | MyType2 | MyType3;
Тогдакомпилятор будет автоматически обрабатывать проверки MyComplexType["type"]
как охрану типа, например:
const exhaustivenessCheck = (x: never) => x;
function someFunction(item: MyComplexType) {
switch (item.type) {
case "type1":
console.log(2 * item.data.b); // okay
break;
case "type2":
console.log(item.data.d.charAt(0)); // okay
break;
case "type3":
console.log(7 - item.data.e); // okay
break;
default:
throw exhaustivenessCheck(item); // okay
}
}
То, что exhaustivenessCheck()
, по сути, является оператором throw
, если функция каким-то образом падает до default
.Этого не должно случиться, но полезность заключается в том, что компилятор предупредит вас, если не решит, что вы все проверили.Это потому, что exhaustivenessCheck()
требует, чтобы его параметр имел тип never
, чего не может быть.Если вы закомментируете предложение case "type3"
или когда-нибудь позже добавите новый компонент в объединение MyComplexType
, строка exhaustivenessCheck()
выдаст ошибку, сообщающую, что вы не проверили регистр.
На этом этапе вы можете остановиться, но если ваши типы действительно настолько программные, поскольку они содержат только два свойства: type
дискриминантную строку и свойство data
, то вы можете определить свои типы с меньшим количеством повторений, например так:
// a mapping from type string to data type
type MyTypes = {
type1: { a: string, b: number };
type2: { c: boolean, d: string };
type3: { e: number, f: boolean };
}
// convert the mapping to the union of types
type MyType<K extends keyof MyTypes = keyof MyTypes> = {
[P in K]: { type: P, data: MyTypes[P] }
}[K]
Вы можете проверить, что MyType
или MyType<keyof MyTypes>
расширяется до объединения MyComplexType
, которое я определил выше.Ваш старый MyType1
теперь MyType<"type1">
и так далее.То есть, если вам нужно использовать ваши старые имена для типов, вы можете сделать это следующим образом:
type MyType1 = MyType<"type1">;
type MyType2 = MyType<"type2">;
type MyType3 = MyType<"type3">
type MyComplexType = MyType;
Надеюсь, это поможет;удачи!