Я предполагаю, что проблема возникает в реализации Search()
.
. Здесь есть ряд проблем, которые мешают компилятору проверить, что то, что вы делаете, является типобезопасным. В конце вам, скорее всего, придется отказаться от такой автоматической проверки безопасности типа c и сказать компилятору не беспокоиться об этом, используя утверждение типа .
Здесь Вот проблемы, которые я вижу:
Внутри реализации Search()
тип props
зависит от неопределенных generi c параметров типа T
и K
; компилятор может достаточно хорошо рассуждать о безопасности типов для указанных типов, но когда он имеет неуказанные обобщенные значения, такие как T
и K
, он не такой умный. В частности, анализ потока управления , такой как защита типов, не влияет на параметры типа c (для получения дополнительной информации см. microsoft / TypeScript # 13995 ). Таким образом, вы можете тестировать props
столько, сколько хотите, и это не даст компилятору понять, что T
следует считать присваиваемым object
(в отличие от object | string | number
) или K
присваивается keyof T
.
Внутри реализации Search()
общее ограничение c для K
равно T extends object ? keyof T : T
, условный тип, который сам по себе зависит от неопределенного универсальный c параметр типа T
. Компилятор особенно плохо умеет манипулировать такими «неразрешенными условными типами». (Существует множество проблем GitHub, в которых эта проблема лежит в основе; вы можете, например, взглянуть на microsoft / TypeScript # 35257 .) Я имею в виду, что даже это не работает:
function foo<T>(t: T) {
const x: number extends T ? T : T = t; // error!
// Type 'T' is not assignable to type 'number extends T ? T : T'
}
Если компилятор не может проверить, что number extends T ? T : T
, по сути, совпадает с T
, он никак не сможет справиться с более сложным случаем.
Вы разбили свойства props
на две отдельные переменные data
и searchKey
. После этого компилятор полностью теряет тот факт, что они коррелируют друг с другом. (См. microsoft / TypeScript # 30581 для получения дополнительной информации.) Это означает, что при проверке, скажем, typeof data[0] === "object"
, компилятор не понимает, что он имеет какое-либо влияние на тип searchKey
. Он обрабатывает data
и searchKey
как полностью независимые переменные.
Чтобы определить, является ли K
keyof T
или T
, необходимо проверить, data[0]
или props.data[0]
относится к типу object
, string
или number
. Эти типы не являются «одноэлементными типами», и поэтому компилятор не будет обрабатывать props
как различимое объединение , даже если вы можете написать его тип как SearchProps<T, keyof T> | SearchProps<string | number, string | number>
. Таким образом, нет замечательного способа воспользоваться преимуществами фильтрации объединений, которую вы получаете при проверке свойств различаемых объединений.
Все это означает, что компилятор действительно не оборудован для проверки типа безопасность для вас. В подобных случаях, когда вы знаете больше, чем компилятор, вам, вероятно, следует использовать утверждение типа:
const Search = <T extends object | number | string, K extends T extends object ? keyof T : T>(
props: SearchProps<T, K>
) => {
const { data, searchKey } = props;
if (typeof data[0] === 'object') {
data[0][searchKey as keyof T]
} else {
data.indexOf(searchKey as T);
}
};
В приведенном выше примере searchKey as keyof T
и searchKey as T
- это утверждения типа, в которых вы сообщаете компилятору что вы знаете о searchKey
, и он просто верит вам и движется дальше. Это должно подавить ошибки. Обратите внимание, что это накладывает на вас бремя проверки безопасности типов, и вы должны серьезно относиться к этой ответственности, поскольку вы не можете ожидать появления предупреждения, если внесете какое-либо изменение, которое сделает ваши предположения недействительными. Например, вы можете изменить (typeof data[0] === 'object')
на (typeof data[0] !== 'object')
, и компилятор не заметит. Так что будьте осторожны.
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код