The setup's real simple: https://tsplay.dev/mMLj1N – basically, if we know that K extends keyof T then why aren't the types compatible?
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
34 messages · Page 1 of 1 (latest)
The setup's real simple: https://tsplay.dev/mMLj1N – basically, if we know that K extends keyof T then why aren't the types compatible?
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
I have asked the question here: https://stackoverflow.com/questions/78091956/narrowing-with-conditional-on-extends-keyof-t
i'd guess it's due to the conditional, i can't really explain past that though
you could rewrite Foo to avoid the conditional though
Preview:```ts
type Foo<K, T> = T[K & keyof T]
// type Foo<K, T> = T[Extract<K, keyof T>]; // this doesn't work either, maybe because Extract uses a conditional
function test1<K, T>(foo: Foo<K, T>) {}
function test2<K extends keyof T, T>(foo: T[K]) {
test1(foo)
...```
This can be super wrong, but I got a suggestion in my editor:
type Foo<K, T> = K extends keyof T ? T[K] : never;
function test1<K, T>(foo: Foo<K, T>) { }
function test2<K extends keyof T, T>(
foo: T extends Record<K, any> ? T[K] : never
) {
test1(foo);
}
This makes sure that K is indeed a key of T at least
it was already made sure by the bound keyof T
surprising that that works...
hmm i think maybe the lack of infer targets is also an issue
I think so yeah.
The error kinda points towards that.
mainly this: 'T[string] | T[number] | T[symbol]' is not assignable to type 'Foo<K, T>'

Preview:ts ... function test2<K extends keyof T, T>(foo: T, bar: K) { test1(foo[bar]) } ...
oh what the hell
i have no idea what's going on there
(use plain ts playground links so they get embedded)
Ah
Preview:```ts
type Foo<K, T> = K extends keyof T ? T[K] : never
function test1<K, T>(foo: Foo<K, T>) {}
function test2<
K extends keyof T,
T extends Record<any, any>
(foo: T, bar: K) {
test1(foo[bar])
}```
Didn know that 🙂 wups
interesting... that'd be nice to avoid though since interfaces don't have explicit index signatures
it still errors with extends object
Yep, I noticed
but yeah that infers correctly too
i think this would end up being never unless T was some string property
Preview:```ts
type Foo<K, T> = K extends keyof T ? T[K] : never
function test1<K, T>(foo: Foo<K, T>) {
return foo
}
function test2<K extends keyof T, T>(
foo: T extends Record<K, any> ? T[K] : never
) {
return test1(foo)
}
test2(() => "")
test2<"toString", string>(() => "")
...```
these all seem to return never
yeah until you give type parameters
test2<'a', { a: string }>('hello')
oh wait still never as return
lol I changed the T extends Record... back to T[K], the return type is correct, but now the foo arg on the inner function is wrong again
Preview:```ts
type Foo<K, T> = K extends keyof T ? T[K] : never
function test1<K, T>(foo: Foo<K, T>) {
return foo
}
function test2<K extends keyof T, T>(foo: T[K]) {
return test1(foo)
}
test2<"toString", string>(() => "")```