У меня есть объект данных, который мне нужно передать в одно из двух мест, в зависимости от того, какой член объединения содержит данный объект. В обоих местах нужны все данные объекта, поэтому воссоздание объекта суженного типа выглядит немного глупо. Хотя, очевидно, это работает.
В качестве альтернативы я попытался сделать интерфейс объекта generi c поверх объединения, чтобы интерфейс мог представлять объект во всех трех местах, полагая, что может применяться автоматическое сужение типа c. к параметру типа TestData
.
interface UserData {
kind: 'user',
user: string,
}
interface ServerData {
kind: 'server',
url: string,
}
type DataTypes = UserData | ServerData
interface TestData<D extends DataTypes> {
data: D,
id: string,
}
Теперь высший класс может использовать TestData<DataTypes>
, а дети могут использовать TestData<UserData>
или TestData<ServerData>
. Это работает нормально, пока вы не попытаетесь передать объект одному из детей. Компилятор правильно сузит свойство TestData
data
, но это не сужает тип реального объекта, который все еще имеет тип TestData<DataTypes>
. Вот пример.
function basicNarrow(test: TestData<DataTypes>) {
if (test.data.kind === 'user') {
// Correctly narrowed to `UserData`
test.data.user
// Error: Generic type not narrowed, still `TestData<DataTypes>
const typed: TestData<UserData> = test
} else {
// Correctly narrowed to `ServerData`
test.data.url
// Error: Generic type not narrowed, still `TestData<DataTypes>
const typed: TestData<ServerData> = test
}
}
В этот момент я мог либо использовать утверждение типа, либо (снова) создать новый объект для передачи правильного типа, но после некоторого поиска я нашел этот ответ для аналогичный вопрос , который дает мне
type NarrowKind<T, N> = T extends { kind: N } ? T : never;
function predicateNarrow(test: TestData<DataTypes>) {
const predicate = <K extends DataTypes['kind']>(narrow: TestData<DataTypes>, kind: K): narrow is TestData<NarrowKind<DataTypes, K>> => (
narrow.data.kind === kind
)
if (predicate(test, 'user')) {
// Correctly narrowed to `UserData`
test.data.user
// Success! Generic type narrowed to `TestData<UserData>
const typed: TestData<UserData> = test
} else {
// Error: Not narrowed
test.data.url
// Error: Generic type not narrowed, still `TestData<DataTypes>
const typed: TestData<ServerData> = test
}
}
Это делает то, что я делал после того, как внутри блока if
, но компилятор не сузится до альтернативного случая в блоке else
без другого явная проверка, как если бы data
была просто локальной переменной.
Вот пример того, что я хотел бы, чтобы суженные типы были в идеале
function idealNarrow(test: TestData<DataTypes>) {
function isKind(/*???*/) { /*???*/ }
if (isKind(test, 'user')) {
const user: UserData = test.data
const typed: TestData<UserData> = test
} else {
const server: ServerData = test.data
const typed: TestData<ServerData> = test
}
}
Любой из решения можно использовать без проблем, но predicateNarrow(...)
- это , поэтому близко к тому, что я искал, есть ли способ как-то объединить эти два поведения, чтобы сузить весь тип generi c TestData<D>
автоматически в блоке else?