В настоящее время TypeScript не может сузить типы параметров универсального типа с помощью анализа потока управления. В идеале я бы хотел написать вашу функцию следующим образом:
function createMyUnionObject<T extends MyUnion["type"]>(
type: T,
value: Extract<MyUnion, { type: T }>["value"]
): Extract<MyUnion, { type: T }> {
return { type, value }; // error ?
}
, но мы не можем (обратите внимание, что нам нужен только один универсальный параметр ... из T
вы должны иметь возможностьдля вычисления value
и возвращаемых типов). Даже если для любого конкретного значения type
и его соответствующего value
компилятор может проверить, что {type, value}
присваивается MyUnion
, он не может сделать это с обобщениями.
Одно препятствие состоит в том, что компилятор подозревает, что ваш type
может иметь полный тип объединения "one" | "two" | "three"
, и, конечно, value
может поэтому быть string | number | boolean
, а затем {type, value}
может не быть назначенным на MyUnion
:
const t = (["one", "two", "three"] as const)[Math.floor(Math.random() * 3)];
const v = (["one", 2, true] as const)[Math.floor(Math.random() * 3)];
createMyUnionObject(t, v); // uh oh
Вы хотите сказать компилятору: нет, T
может быть только либо "one"
, или "two"
, или "three"
, но не объединение двух или более из них. Это еще не часть языка, но было запрошено 1035 *. Если это когда-либо будет реализовано, его все равно нужно будет объединить с неким анализом потока управления, который сделал несколько проходов по телу, делая каждое возможное сужение по очереди. Я предложил это (и связанные вещи ), но опять же, это еще не часть языка.
Пока что я бы сказал, чтоесли вы хотите, чтобы ваш код компилировался и двигался дальше, вам нужно будет использовать утверждение типа , чтобы просто сказать компилятору, что вы знаете, что вы делаете безопасно (и что вы просто не собираетесьбеспокойтесь о ком-то, кто делает сумасшедшие Math.random()
вещи, подобные приведенному выше коду):
function createMyUnionObject<T extends MyUnion["type"]>(
type: T,
value: Extract<MyUnion, { type: T }>["value"]
) {
return { type, value } as any as Extract<MyUnion, { type: T }>; // assert
}
Это должно сработать, и вы можете использовать createMyUnionObject()
, как хотите. Хорошо, надеюсь, это поможет. Удачи!
Ссылка на код