Considering that ValueType<T, K>
is categorized as a conditional type, and given that the type of value
is
ValueType<RollingStockSelectorParams, K>
for a
generic K
within the scope of function
f()
, it can be concluded that
value
falls into the category of a
generic conditional type. The TypeScript type checker typically holds off on evaluating such types extensively. Instead, it treats the type as somewhat opaque, only raising objections if an attempt is made to assign anything other than the same opaque type.
When attempting to use it in the manner depicted, the type checker reaches a point where it declares the assignment not feasible. The specific structure of ValueType<T, K>
employs conditional type inference to derive the outcome. Since the resulting U
type is inherently constrained to unknown
by default, the compiler raises caution due to the potential wide variability of the U
type being equated with unknown
.
There have been numerous mentions in GitHub issues about generic conditional types regarding this behavior. In essence, there are limitations in addressing this scenario comprehensively, though there may be isolated instances where support could be extended. One instance closely resembling the current context is highlighted in microsoft/TypeScript#52489.
If you are certain that an array type is involved, there's no necessity for employing conditional type inference to ascertain its element type. Utilizing indexing with number
would suffice:
export type ValueType<T extends Record<K, any[]>, K extends keyof T> =
T[K][number]
While this approach might not directly address your situation, at least the compiler would identify the result as string | null
rather than merely unknown
.
Inquiries raised on Stack Overflow should ideally focus on a single question. Hence, I won't delve deeply into your secondary query. To briefly explain why indexOf()
anticipates Comfort
, this pertains to the intersection of possible element types. Essentially,
Comfort & string & (string | null)
simplifies to just
Comfort
. Due to the challenge in interpreting
additionalRsParams[title].indexOf()
generically, the compiler reverts to the specific constraint, leading to a
union of functions that necessitates an intersection of arguments, in line with
enhanced calling behavior for union types.
The recommended coding practice, as discussed in microsoft/TypeScript#47109, advocates building everything around generic indexing within a fundamental key-value interface or mapped types based on that interface. Here's how it looks:
interface Rolling {
comfortLevels: Comfort,
tractionModes: string,
electricalProfiles: string | null,
powerRestrictions: string | null
}
function g<K extends keyof Rolling>(
value: Rolling[K],
title: K,
additionalRsParams: { [K in keyof Rolling]: Rolling[K][] }
) {
const index = additionalRsParams[title].indexOf(value);
}
However, I refrain from delving further since the primary inquiry isn't focused on "how to make the compiler understand this", but rather on questioning why value
is perceived as unknown
.
Access the code in Playground