@@ -108,8 +108,173 @@ All of the above work as expected, correctly indexing the property `x` of the ob
-[keyof type operator on TypeScript handbook](https://www.typescriptlang.org/docs/handbook/2/keyof-types.html).
## References
## keyof with getProp() function
Here's function that takes an prop name and an object, and we return the value of that prop.
We use `typeof` to help us limit the name of possible keys based on the object passed.
```typescript
typeJedi={
name:string;
skills:string[];
points:number;
};
functiongetProp<
ObjType,
PropextendskeyofObjType,
>(propName:Prop,obj:ObjType):ObjType[Prop]{
returnobj[propName];
}
declareconstluke:Jedi;
getProp("point",luke);
//
// ERROR:
//
// Argument of type '"point"' is not assignable to parameter
// of type 'keyof Jedi'.
////
getProp<Jedi,keyofJedi>("points",luke);
//
// OK because ‘points’ is a key in the type ‘Jedi’.
////
```
Note how we use `Prop extends keyof ObjType` as the second generic type parameter in the definition of `getProp()` and then the parameter `propName` anotated with that type `Prop`.
It causes the type checker to only allow property names that actually exist in the type `ObjType`, which in our example is bound to the type `Jedi`.
-[TS Playground for getProp() with keyof in prop name type](https://www.typescriptlang.org/play?#code/PTBQIAkIgIIQQVwC4AsD2AnAXBAYgU3QDsBDQgE1QgCFiBnW1cYaCZRRAB1sxADMCS5VACM6DAHRk8AN2ABjVIUTE5iMJBhtO3EAHd9kmWl2JUh2U1ChEATw54IAKTxkAlhAC8EAN6gIEEgBbPGxaRHRXQgBzAG4-CFoAa1cAGxTuBPDIqIBtAF04-w5USMQMwnhA4QI4gF840F54QlVXRQgovEQABXRUDgAeeIB5YQArABU7PAAaeN7+iDwAD0Q8cloIRLwbVF4IUcnpuYA+AAoOPo4AOWJg7AWOGYgRMexDqfsASnfxz7wco88j54uguvAiC9xjlLv1bsECqBalZQFI5CliGCIApCGEICl4NtsM43A1Oj0rmcAETFUpU54E7ZfOIgJgQACiACVOcNOZhLJBYOgopV1ogXvtbPYIAByGklJRUmUQVybQiocViVxREjCFIOUwQDiYu5dAhsvYQKUOGXbXb7EmuGXiSygcmPAaO552y2O85UgZZKKddAqpR4NKq9a0BzIAh4E70-GEvDMqwgDOZrPZnO5vP5guF9TMCZ4PEAYToZbZAFo6-WG5ZXIFiuhxdafByAI7wYgpZ7s5b2VQQWoQXh9QIQKkAAWtNbkyD7+uiZeASFStCpDRxeMZeHhDi87spVKCeCT+7Tu-F+4Aysk0ptj11HtSkql0peU9fFHuU90CplJ4HSvqetJKFuDI-g0HZyFWz4QDk8SDsOiADOyPZ9oG0yWvuh7PGEETRCcJxzP4qF4KoGFYSkOH2HhKYPp+tCEVk0T5KR5EckOVHoZhvZ0dajHbIBpSsQElTVOgXGgIiQA).
## keyof in type predicates
In this next example, we have a function that displays a prop from an object, but its parameter types are very permissive:
```typescript
functiondisplayObjProp(
prop:string|number|symbol,
obj:Record<string,unknown>,
):void;
```
Note that `prop` is `string | number | symbol`.
Those are the ways we can index the keys of an object in JavaScript, so, TypeScript has to consider them all when indexing object types.
But we can create a type predicate to make sure the prop really exists in the object.
```typescript
/**
* Type predicate to check whether `key` exists in `obj`.
*/
functionisKey<Obj>(
key:string|symbol|number,
obj:Obj,
):keyiskeyofObj{
returnkeyinobj;
```
Note the use of `keyof` in the predicate return type!
We can make use of this type predicate (a.k.a type guard) inside `displayObjProp()` to make sure we only call `getProp()` if we know the object is sure to contain the given key:
* Type predicate to check whether `key` exists in `obj`.
*/
functionisKey<Obj>(
key:string|symbol|number,
obj:Obj,
):keyiskeyofObj{
returnkeyinobj;
}
/**
* Returns the value of the property `propName` from `obj`.
*/
functiongetProp<
Obj,
PropextendskeyofObj,
>(
propName:Prop,
obj:Obj,
):Obj[Prop]{
returnobj[propName];
}
/**
* Displays the value of a property.
*
* **NOTE**: This is a dummy, convoluted and simple example so
* we can focus on the types.
*/
functiondisplayObjProp(
prop:string|number|symbol,
obj:Record<string,unknown>,
):void{
if (!isKey(prop,obj))return;
log(getProp(prop,obj));
}
constluke:Jedi={
name:"Luke Skywalker",
skills:["The Force","Lightsaber"],
points:97,
};
displayObjProp("z",{x:1,y:2});
//
// Prints nothing.
////
displayObjProp("name",luke);
//
// → "Luke Skywalker".
////
```
[TS Playground isKey() type predicate](https://www.typescriptlang.org/play?ssl=67&ssc=4&pln=7&pc=1#code/PTBQIAkIgIIQQVwC4AsD2AnAXBAYgU3QDsBDQgE1QgCFiBnW1cYaCZRRAB1sxADMCS5VACM6DAHRk8AN2ABjVIUTE5iMJBhtO3EAHd9kmWl2JUh2U1CgFhWoggAbVAHNsAYUUMHeANoAiJ2c-AF0IAF4IGy88cUDxYQBLcgAKKNRvAEoAbitEAE8OPAgAKTwyBPCIAG9QCAgSAFs8bDt0JOccutoAawSHB24IVvafYM6IDlQkxEHCeAbhAhyAXxzwACp12vWIABUCoo50MoS5YkQi00jkPDluiF0blAIIAANuvDzXiDwADwS7LQIEk3iIAFavcTbMC8eCEVQJRTA2gAaU+AB4APLCMEAPmStQgHzyLUQbUIzggAB8hnkFulqfV5ot0AAaQng7DYsHsjLYYnIomfVC8CDc6qE46IeBEIV5YGECDglZWYCbbYQABKeGlRCBzwg0mIDngRRFEANR1QhXQ+TeVo4ADliE1vrx0KgGqCcZDoaBYfDEIjFc4dQAFD0cdGE7nsuoR60-X4XchA4nm2OgfGEh3OprYBMcONKnFcnG8stgnyF0I1OpSmWK8E+XMuvBjUDLVXqiA7AAiAI4DmIeX1N0NxtNStFxAmkYI+ShvYgGs2jsxuwAoptsLtkADBbOyPMGnlWZFFNJ0kgyhBSGQhgkGkOin8XS+hoxl7oimdFbxUDkeAgSRA18kKWgl3WGE4QRJFyloIcR25QsCTqB1SXJSkaTmBYXhpWg6WEdJi05LVbgwMh0WGClzzhbpCFQXRCFxCtDSmB862BUVkgAQgBNE8mSB1z3BDIMggBsiDWOpAmSUNEFQkSSzBcSVWsTx7BND5sFKcpKi4xpmggPwABl4A+CAAGVujyXRjQ+dA-GLHo+gGbB-D3IocAwOQ8GckzTISZw2FoYgWRCYtJmmQYAE4AHZ2VWKwEKQvIUMjZI-AALwCqoIF+bAAEZzxJCAACYIGWbJLEgCMYvqVAUHaKEQBAFLB2HdKcVQvwjIC7S8Bq9rIEAJMJAosoobLshyCD8Vq2tAIA).