I\'ve been trying to make a generic function which receives and object T and receives a string property name of that object T.
I used https://www.typescriptlang.org/d
The compiler generally cannot determine assignability of unresolved conditional types (that is, conditional types which cannot be eagerly evaluated because at least one of the T
or U
in T extends U ? V : W
is not yet fully specified).
This is more of a design limitation than a bug (see Microsoft/TypeScript#30728); the compiler is not going to be as smart as a human being (note to self: come back here when the machine uprising happens and edit this) so we shouldn't expect it to just "notice" that T[TypedPropertyName<T,P>] extends P
should always be true. We could write a particular heuristic algorithm to detect the situation and perform the desired reduction, but it would have to be able to run very quickly so that it doesn't degrade compile times for the 99% of the time when it wouldn't be useful.
Can anyone point me in the direction of making the generic version work (without any hacks)
That really depends on what you consider a hack. The absolute simplest thing to do is to use a type assertion, which is explicitly intended for times when you know something is type safe but the compiler isn't able to figure it out:
function generic<T>(form: T, field: StringPropertyNames<T>): string {
return form[field] as any as string; // I'm smarter than the compiler
Honestly, I do not know what the issue is. You might try filing a issue on their GH. However, I do know that the following does work without explicitly specifying the return type:
function generic<T>(form: T, field: StringPropertyNames<T>) {
return form[field];
}
and it even correctly types the return value as a string:
const test = {
a: "b",
c: 1,
"other": "blah"
}
generic(test, "a").charAt(0) //passes - "b"
generic(test, "a") * 5 // fails - function result is not a number
generic(test, "c") //fails - "c" is not assignable to "a" | "other"
I would additionally recommend this addition to make sure the first argument must be an object:
function generic<T extends object>(form: T, field: StringPropertyNames<T>) {
return form[field];
}