When I do this:
interface I<T extends Record<string, {b: boolean}>> {
x: T[keyof T]['b'];
}
everything works, but as soon as I change string
to number
, I get ts(2536): Type 'b' cannot be used to index type 'T[keyof T]'
. Why is that?
CodePudding user response:
I believe the reason for this behaviour is that Record<number, any>
also does accept array types.
Doing I<{b: boolean}[]>
compiles correctly. But using keyof
on an array type will also include a lot of other properties like length
or method keys like pop()
or push()
. So when you do T[keyof T]
, you will also get the types of these properties.
type ArrayValueof = {b: boolean}[][keyof []]
// type ArrayValueof = number | {
// b: boolean;
// } | (() => IterableIterator<{
// b: boolean;
// }>) | (() => {
// copyWithin: boolean;
// entries: boolean;
// fill: boolean;
// find: boolean;
// findIndex: boolean;
// keys: boolean;
// values: boolean;
// }) | ... 28 more ... | ((searchElement: {
// b: boolean;
// }, fromIndex?: number | undefined) => boolean)
As you can see, { b: boolean }
is part of this union but not every element in this union is an object with the property b
. That's why you can not use "b"
to index T[keyof T]
when T
also might be an array.
You can fix this issue by using an additional check:
interface I<T extends Record<number, {b: boolean}>> {
x: T[T extends any[] ? number : keyof T]["b"];
}