Repository navigation
Closed
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Description
TypeScript Version: 3.4.0-dev.20190403
Code
function toHTML<T, K extends keyof T>(items: T[], key: T[K] extends T[] ? K : never) {
if (!items.length) return '';
// i[key] here is an error :(
const innerHTML = items.map(i => `<li>${i}${toHTML(i[key], key)}</li>`).join('\n');
return `<ul>\n${innerHTML}\n</ul>`;
}
class Item {
constructor(public name: string, public nodes: Item[]) { }
toString() { return this.name; }
}
const tree = [
new Item("Root", [
new Item("Node 1", [new Item("Subnode 1", [])]),
new Item("Node 2", [])
])
];
// This works correctly! Anything but 'nodes' (eg. 'name') is an error.
document.write(toHTML(tree, 'nodes'));Idea: Take a list of type T and a key K into type T that returns a T[] and recursively walk the entire tree.
Expected behavior:
If I specify that a keyof T must extend T[] (or it becomes never), I can use it as such.
Actual behavior:
It partially works, but indexing into the T type with K shows the error:
test.ts:4:56 - error TS2345: Argument of type 'T[T[K] extends T[] ? K : never]' is not assignable to parameter of type 'T[]'.
4 const innerHTML = items.map(i => `<li>${i}${toHTML(i[key], key)}</li>`).join('\n');
~~~~~~
Reacted by monnef, Greg and JesseHalpernTR
Metadata
Metadata
Assignees
Labels
Design LimitationConstraints of the existing architecture prevent this from being fixedConstraints of the existing architecture prevent this from being fixed
Activity
jcalz commentedon Apr 3, 2019
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
T[T[K] extends U ? K : never]is assignable toU. But it would cost something in terms of type checker complexity and compile time, and any benefit would only be seen by some fraction of users who specifically do this kind of thing. It could be worth it, but I wouldn't hold my breath.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:
Two: walk the compiler through the type safety of the situation by giving it some generic types that it does properly check:
Note that in both of the above cases, I changed the
Ktype parameter to be of typeKeysMatching<T, T[]>instead of justkeyof T, since that's the constraint you actually want to impose. This is probably more useful to callers oftoHTML()who would like some auto-completion in their IDEs (i.e., you want to see just"nodes"as a suggested value forkey, and not"nodes" | "name"and then give you an error when you enter"name".)Hope this helps you. Good luck!
blixt commentedon Apr 3, 2019
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
nevers etc). Thank you! The original issue could still be fixed, but you're right it would put more work on the type checker.