Closed
Description
TypeScript Version: 2.8.3 and 2.9.1 (I think. The actual version in playground)
Code
The next code works as expected.
interface I {
property: number;
method1(): void;
method2(a: string): number;
}
declare const ob: I;
declare function foo<T, K extends keyof T>(ob: T, key: K): T[K];
const f1 = foo(ob, 'property'); // number
const f2 = foo(ob, 'method1'); // () => void
const f3 = foo(ob, 'method2'); // (a: string) => number
// f1(); OK, not a function
f2();
f3('a string');
But I want to restrict the key
parameter in foo
to just functions.
Since #21316, and as an example on that PR, we can use something like FunctionPropertyNames
.
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
declare function bar<T, K extends FunctionPropertyNames<T>>(ob: T, key: K): T[K];
// const f4 = bar(ob, 'property'); OK, 'property' is not allowed
const f5 = bar(ob, 'method1'); // () => void | (a: string) => number
const f6 = bar(ob, 'method2'); // () => void | (a: string) => number
// ERROR! Both are of type '() => void | (a: string) => number'
f5();
f6('a string');
// Type assertion
const f7 = bar(ob, 'method1' as 'method1'); // () => void
const f8 = bar(ob, 'method2' as 'method2'); // (a: string) => number
// OK again
f7();
f8('a string');
Expected behavior:
f5
and f6
inferred to () => void
and (a: string) => number
, respectively, without need a type assertion (pretty much as the first snippet).
Actual behavior:
f5
and f6
are both inferred to () => void | (a: string) => number
, which is very odd since I'm passing a literal string.
Playground link
Playground link
Related Issues:
Maybe #24080 (but that seems related to the keyof T
~ string | number | symbol
issue and my example fails with TS 2.8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment