-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Argument of type 'T[T[K] extends T[] ? K : never]' is not assignable to parameter of type 'T[]'. #30728
Comments
I think there's only so much we can expect the compiler to understand about manipulating conditional types that depend on generic type parameters. Maybe the compiler could be made to specifically check that Meanwhile, there are two general ways for you to deal with this. One: a judicious use of a type assertion to tell the compiler that it is not as clever as you: type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T]
function toHTML<T, K extends KeysMatching<T, T[]>>(items: T[], key: K) {
if (!items.length) return '';
const innerHTML = items.map(i => `<li>${i}${toHTML(
i[key] as any as T[], // I'm smarter than you, compiler! 🤓
key)}</li>`).join('\n');
return `<ul>\n${innerHTML}\n</ul>`;
} Two: walk the compiler through the type safety of the situation by giving it some generic types that it does properly check: type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T]
// constrain T to be a subtype Record<K, T[]>, so T[K] will be recognized as T[] by the compiler
function toHTML<T extends Record<K, T[]>, K extends KeysMatching<T, T[]>>(items: T[], key: K) {
if (!items.length) return '';
const innerHTML = items.map(
i => `<li>${i}${toHTML(i[key], key)}</li>`
).join('\n');
return `<ul>\n${innerHTML}\n</ul>`;
} Note that in both of the above cases, I changed the Hope this helps you. Good luck! |
Hey this is very nice! I had started doing something like this but it didn't work out for me (types can get a bit finicky, like when filtering out |
TypeScript Version: 3.4.0-dev.20190403
Code
Idea: Take a list of type
T
and a keyK
into typeT
that returns aT[]
and recursively walk the entire tree.Expected behavior:
If I specify that a
keyof T
must extendT[]
(or it becomesnever
), I can use it as such.Actual behavior:
It partially works, but indexing into the
T
type withK
shows the error:Playground Link: http://www.typescriptlang.org/play/#src=function%20mapTree%3CT%2C%20K%20extends%20keyof%20T%2C%20R%3E(nodes%3A%20T%5B%5D%2C%20childrenKey%3A%20T%5BK%5D%20extends%20(T%5B%5D%20%7C%20undefined)%20%3F%20K%20%3A%20never%2C%20fn%3A%20(item%3A%20T)%20%3D%3E%20R)%3A%20R%5B%5D%20%7B%0D%0A%20%20%20%20const%20result%3A%20R%5B%5D%20%3D%20%5B%5D%0D%0A%20%20%20%20for%20(const%20node%20of%20nodes)%20%7B%0D%0A%20%20%20%20%20%20%20%20fn(node)%0D%0A%20%20%20%20%20%20%20%20const%20children%20%3D%20node%5BchildrenKey%5D%0D%0A%20%20%20%20%20%20%20%20if%20(children)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20This%20does%20not%20work%20even%20if%20we%20validate%20that%20T%5BK%5D%20is%20always%20T%5B%5D.%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20result.push(...mapTree(children%2C%20childrenKey%2C%20fn))%0D%0A%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20return%20result%0D%0A%7D%0D%0A%0D%0Ainterface%20MenuItem%20%7B%0D%0A%20%20%20%20id%3A%20number%0D%0A%20%20%20%20label%3A%20string%0D%0A%20%20%20%20submenu%3F%3A%20MenuItem%5B%5D%0D%0A%7D%0D%0A%0D%0Aconst%20menu%3A%20MenuItem%5B%5D%20%3D%20%5B%5D%0D%0A%2F%2F%20This%20works%3A%20changing%20submenu%20to%20something%20else%20is%20an%20error.%0D%0Aconst%20allLabels%20%3D%20mapTree(menu%2C%20%22submenu%22%2C%20item%20%3D%3E%20item.label)%0D%0A
The text was updated successfully, but these errors were encountered: