Компилятор просто не достаточно умен.Условные типы, которые зависят от универсальных параметров (например, KeysOfType<T, string>
), обычно обрабатываются компилятором как непрозрачные, и хотя , вы понимаете, что KeysOfType<T, V>
был специально сконструирован так, чтобы гарантировать, что T[KeysOfType<T, V>] extends V
правда, компилятор даже не пытается.
Самое общее решение, доступное нам в подобных случаях, - это использование утверждения типа .Например, вы можете сказать компилятору не беспокоиться и рассматривать obj[p]
как string
:
function getLen<T>(obj: T, p: KeysOfType<T, string>): number {
return (obj[p] as unknown as string).length;
// the type T[{ [P in keyof T]: T[P] extends string ? P : never; }[keyof T]]
// is so opaque to the compiler that we must widen to unknown
// before narrowing to string
}
Обратите внимание, что вы освобождаете компилятор от обязанности проверки безопасности типов.Вы могли бы так же легко сказать obj[p] as unknown as boolean
, и компилятор поверил бы вам.Так что используйте эту мощность с осторожностью.
Другой способ сделать подобное - использовать одну перегрузку функции , чтобы различать общий условный тип, который видят вызывающая сторона инадеюсь, более гибкий тип, как видно из реализации:
// call signature, unchanged
function getLen<T>(obj: T, p: KeysOfType<T, string>): number;
// implementation signature... let's make p the generic type K
// and say that obj has keys K and values of type string
function getLen<K extends keyof any>(obj: Record<K, string>, p: K): number {
return obj[p].length;
}
Причина, по которой он похож на утверждение типа, заключается в том, что компилятор позволяет вам сделать сигнатуру реализации более свободной, чем сигнатуры вызова ... если вы 'не будьте осторожны, вы можете лгать компилятору, и вы не увидите проблемы до времени выполнения.
Хорошо, надеюсь, это поможет.Удачи!