title: Infer object's property type when access by key of specific type
body: <p>I have helper type for extract keys of specific type:</p>
  
  <pre class="lang-js prettyprint-override"><code>/**
   * From type S extract those keys that assignable to type T
   */
  type KeysOfType&lt;S, T&gt; = {
    [K in keyof S]: S[K] extends T ? K : never;
  }[keyof S];
  </code></pre>
  
  <p><strong>Problem</strong></p>
  
  <p>When accessing by these keys typescript cannot infer that object property is of type T.</p>
  
  <p><strong>Question</strong></p>
  
  <p>It is possible to write function which accepts object and object's keys of specific without type-casts when accessing by these keys?</p>
  
  <pre class="lang-js prettyprint-override"><code>interface EntityState&lt;T&gt; {
    ids: string[];
    entities: { [id: string]: T };
  }
  
  function collectIds&lt;T&gt;(obj: T, keys: KeysOfType&lt;T, EntityState&lt;unknown&gt;&gt;[]): string[] {
    const ids: string[] = [];
    for (const key of keys) {
      const state = obj[key];
      // TS2339: Property 'ids' does not exist on type 'T[{ [K in keyof T]: T[K] extends EntityState&lt;unknown&gt; ? K : never; }[keyof T]]'.
      ids.push(...state.ids); // &lt;-- state not inferred as EntityState&lt;unknown&gt;
    }
  
    return ids;
  }
  
  const state: {
    status: boolean;
    first: EntityState&lt;{ value: string; }&gt;;
    second: EntityState&lt;{ prop: string; }&gt;;
  } = {
    status: true,
    first: {
      ids: ['1'],
      entities: {
        '1': { value: 'value from first state' }
      }
    },
    second: {
      ids: ['id_from_second_state'],
      entities: {
        'id_from_second_state': { prop: 'prop from second state' }
      }
    }
  };
  
  console.log(collectIds(state, ['first', 'second']));
  </code></pre>
  
  <p><a href="https://www.typescriptlang.org/play/?ts=3.7.5#code/PQKhFgCgAIWgxATgewLbQC4E8AOBTaAZWjwA8NEBDAYw0wAtkBnAgazyyYcrsqaYCWAcwB2lAEYAbAhmSZcBACpRYwKNnzQA0hyYB5AGaKFAHkIAaaIoB80ALzQA3iugBtLdAEjo7LMgNEALoAXETugSTkeCIAJlyK0AD82tChIngAbniIANxQAL6uvv5BeZBQXhjZBjQEAKIiGALYhBg8eCY2Ti4CcaFMFF5CroFl0CSNzQJ4TKGObr39gyJCIVbQ%20WX5UFAGAK4itALI3tTIktK0AJJxndYAFMjiAFahipa%20s9q6hsb4nZYGk0Wm0qiYDqwRMgAO4iazWEYASiWiCGI26MGgZxEA08fWgA1RK3RDhGY2gBmQiGg92xuN80BKn0RGPG4zpdAG7XsjJeRQ4oxc42AwCshAATABmSUATlCAAUUPhENhoAByXpMNXQGLIGbQKF0MgCXEneSaNWKVzzdyebzFAKKNZWrQRMhVWJcIHNLCtdrgkSQmFwpIpNKZbI5Db8vyOwKBNUAOiFeKYiZweyY9HuidzXKqic1iKjIugJgAtOXec8YxFDXaDNlEHgYtA%20NBvSD-RCobDrC5tuVMc2MHtEN5NVsdpAOQTQXg5i585nQuJkOc8JQROSDAJEANQp3ffOTPMMpRJHsF3OiUIo-lrOSWNiYofJl2wfMcEqUUN74%20Ch5ZxMWXL4KCvcwXF3fcMEXTFxk1UJXDVABGNVAkg%20CJmBaYvmAtk2VQtU5mgc9L2vNUyKvCkUHQaDcXzPBtUHAiWI2TDxmfE5X1ZNlELcDUYgAfQMWihK42JxPndCOLZaIcJmOCCPGQSRLEiThMY4inGgb9kBwUI1T0nAaLQAk8BfOd2mYlM2MHTZpzpDdE0kZAhFpc5LgwG4mHuRjLGQ%20iMDVSw1Q09DEWLKAgA" rel="nofollow noreferrer">TypesScript Playground link</a></p>