Я хочу создать предикат типа, чтобы при использовании filter в массиве дженериков Union он возвращал правильный тип.
Я написал тип, указывающий тип ввода/вывода функции преобразования для соответствующего идентификатора:
type ConverterGroupIdToFunc = {
text: [string, string]
image: [File, string]
}
type ConverterGroupId = keyof ConverterGroupIdToFunc
type ConverterGroup<T extends ConverterGroupId> = {
id: T
convertFunc: (s: ConverterGroupIdToFunc[T][0]) => (ConverterGroupIdToFunc[T][1]);
}
И еще мне нужен массив ConverterGroup для хранения двух разных типов дженериков (ConverterGroup<"text">|ConverterGroup<"image">)[]
type ConverterGroupUnion = ConverterGroup<"text"> | ConverterGroup<"image">
Для converterGroups: (ConverterGroup<"text">|ConverterGroup<"image">)[] я бы хотел, чтобы find() возвращал тип ConverterGroup<"text">, если id группы конвертеров также равен "text".
Я пытаюсь использовать предикат типа, чтобы указать find() вернуть правильный тип, но не нашел, как:
function isConverterGroup<T extends ConverterGroupId>(converterGroupId: T) {
return (converterGroup: ConverterGroupUnion): converterGroup is ConverterGroup<T> => {
return converterGroup.id === converterGroupId;
}
}
const convertFunc = converterGroups.find(isConverterGroup("text"))!.convertFunc;
Приведенный выше код дает мне ошибку:
A type predicate's type must be assignable to its parameter's type.
Type 'ConverterGroup<T>' is not assignable to type 'ConverterGroupUnion'.
Type 'ConverterGroup<T>' is not assignable to type 'ConverterGroup<"text">'.
Types of property 'id' are incompatible.
Type 'T' is not assignable to type '"text"'.ts(2677)
@jcalz Отлично! Оно работает! Конечно, я могу отредактировать свой вопрос.






Иногда вы знаете, что тип X можно назначить типу Y, но TypeScript не может этого увидеть, потому что либо X, либо Y зависят от некоторого общего типа. В вашем примере ConverterGroup<T> является общим, и компилятор не может определить, что его можно назначить ConverterGroupUnion. А поскольку функция защиты пользовательского типа требует, чтобы для y is X тип X был назначен typeof y, она жалуется на converterGroup is ConverterGroup<T>.
В подобных ситуациях вы обычно можете это исправить, изменив X на X & Y, Extract<X, Y> или Extract<Y, X> в зависимости от варианта использования. Пересечение всегда присваивается его членам, как и результат объединения -фильтрации Извлечь тип утилиты . Если вы правы, что X можно присвоить Y, то в конечном итоге X & Y и Extract<X, Y> (и, возможно, Extract<Y, X>, в зависимости от формы Y и X) будут просто X, как только будет указан общий тип, и результирующее поведение не изменится. Если вы ошиблись насчет назначаемости, то в итоге вы получите что-то другое, возможно никогда, поэтому вам следует быть осторожным и знать, что вы делаете с типами.
В любом случае, поскольку в вашем случае похоже, что вы просто хотите отфильтровать ConverterGroupUnion до члена, который можно назначить ConverterGroup<T>, вы можете использовать Extract<ConverterGroupUnion, ConverterGroup<T>>:
function isConverterGroup<T extends ConverterGroupId>(converterGroupId: T) {
return (converterGroup: ConverterGroupUnion):
converterGroup is Extract<ConverterGroupUnion, ConverterGroup<T>> => {
return converterGroup.id === converterGroupId;
}
}
Теперь ошибок нет, а другой код работает как положено:
const convertFunc = converterGroups.find(isConverterGroup("text"))!.convertFunc;
// const convertFunc: (s: string) => string
Обратите внимание: если вы сделали что-то странное, например
const x = isConverterGroup(Math.random() < 0.5 ? "text" : "image");
// const x: (converterGroup: ConverterGroupUnion) => converterGroup is never
Вы понимаете, что never я упомянул, потому что ConverterGroup<"text" | "image"> не связано ни с ConverterGroup<"text">, ни с ConverterGroup<"image">, поскольку ConverterGroup<T> инвариантно в T (см. Разница между дисперсией, ковариацией, контравариантностью, бивариантностью и инвариантностью в TypeScript). Что является одной из причин ошибки в первую очередь.
Соответствует ли такой подход вашим потребностям? Если да, то я напишу ответ с объяснением (хотя у меня нет знаний по реагированию, и не похоже, что вопрос действительно от этого зависит... минимальный воспроизводимый пример действительно должен содержать минимум вещей, необходимых для демонстрации проблема, так что, возможно, вам захочется потратить некоторое время отредактировать на ее запись). Если нет, то что мне не хватает?