You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Narrow "string" to "keyof T" with "in" guard? #12892
Should if (x in t) {} narrow the type of x to be keyof T inside the conditional block?
TypeScript Version: 2.1.4
Code
interfaceSpec<T>{description: string;default: T;}functiongetDescription<T>(specs: {[kinkeyofT]: Spec<T[k]>},k: string){if(kinspecs){returnspecs[k].description;// error TS2536: Type 'string' cannot be used to index type '{ [k in keyof T]: Spec<T[k]>; }'.}returnnull;}
Expected behavior:
This should type check. k should be narrowed from string to keyof T inside the if (k in specs) guard.
Actual behavior:
The type of k remains string inside the guard, leading to the indexing error.
Aleksey-Bykov It is actually not something that extends keyof T; it's the opposite, and keyof T is the lower bound. Adding more elements to the union will make it a supertype (i.e. a less-specific type).
You can capture the type of specs as a generic type, and constraint k to be keyof that. if you still want to drill in and get a property type you can use lookup types, e.g.:
exportinterfaceMap<T>{[x: string]: T}interfaceSpec<T>{description: string;default: T;}functiongetDescription<SextendsMap<Spec<any>>,KextendskeyofS>(specs: S,k: K){if(kinspecs){returnspecs[k].description;// error TS2536: Type 'string' cannot be used to index type '{ [k in keyof T]: Spec<T[k]>; }'.}returnnull;}
For getting the type of default, for example, you can write it as such:
functiongetDefault<SextendsMap<Spec<any>>,KextendskeyofS>(specs: S,k: K): S[K]["default"]{if(kinspecs){returnspecs[k]["default"];// error TS2536: Type 'string' cannot be used to index type '{ [k in keyof T]: Spec<T[k]>; }'.}returnnull;}getDefault({x: {description: "s",default: 1},y: {description: "s",default: "ss"}},"x");// numbergetDefault({x: {description: "s",default: 1},y: {description: "s",default: "ss"}},"y");// string
The reason why narrowing is not really correct is S can have more properties at runtime than we know about at design time. so if we narrow k to keyof specs it would be more restrictive than users intend.
Activity
zpdDG4gta8XKpMCd commentedon Dec 13, 2016
yes it should as it was proven at runtime to be true
(maybe not
keyof Titself, but something that extendskeyof T)DanielRosenwasser commentedon Dec 14, 2016
Aleksey-Bykov It is actually not something that extends
keyof T; it's the opposite, andkeyof Tis the lower bound. Adding more elements to the union will make it a supertype (i.e. a less-specific type).zpdDG4gta8XKpMCd commentedon Dec 14, 2016
true, i always forget that unions are the other way around
mhegazy commentedon Jan 23, 2017
You can capture the type of
specsas a generic type, and constraintkto bekeyofthat. if you still want to drill in and get a property type you can use lookup types, e.g.:For getting the type of default, for example, you can write it as such:
mhegazy commentedon Jan 23, 2017
The reason why narrowing is not really correct is
Scan have more properties at runtime than we know about at design time. so if we narrowktokeyof specsit would be more restrictive than users intend.danvk commentedon Jan 24, 2017
Thanks for the explanation, Mohamed Hegazy (@mhegazy)