TS beginner. Here
let test = <T,>(x:T, c:{[k in keyof T]:(p:T[k])=>void})=>{
}
test({name:"john"}, {name:(functionParam)=>null})
The functionParam
is resolved as string
. But I used T[k]
which refers to "john"
. So is it correct that it got inferred as string
?
Where can I read more about this?
CodePudding user response:
You are observing type widening which happens in this case because there is no constraint on T
and T
also does not directly infer the string type. You can read more about the specifics in this Pull request.
To solve this, add an extra generic type S
which extends string
(or any other primitive type which may be valid here). T
will be constrained to Record<string, S>
.
let test = <
T extends Record<string, S>,
S extends string
>(x: T, c:{ [K in keyof T] : (p:T[K]) => void })=> {}
test({ name:"john" }, { name:(functionParam) => null })
// ^? "john"
Now, the parameter functionParam
is correctly inferred to the string literal type "john"
.
CodePudding user response:
I'm not aware of a specific name of this features. It's the keyof operator you apply here.
The keyof operator takes an object type and produces a string or numeric literal union of its keys.
Your object type T
is { name: "john" }
. The result of keyof
is hence "name" and T[k]
returns therefore "john", which is widened to string
.
CodePudding user response:
Yes, this is correct. In TypeScript, you can index a type to get the type of the property of that key (see Indexed Access Types). Basing a type on the value of an object's property is impossible, as TypeScript runs at compile time and values are determined at runtime. If you had a type "john"
then that is what would show up:
type John = {name: "john"};
let yourName: John["name"];
You can see that the type of yourName
is then "john"
. By default, TypeScript assumes that the object will change, so the type of
let me = {name: "john"};
is inferenced as {name: string}
(TypeScript Playground). If it was {name: "john"}
by default, then you couldn't change it to something else. Most of the time, when you declare an object, you're going to change it, so this is the safe assumption to make.