title: How to narrow down a generic type based on object property type
body: <p>I want to create a Typescript utility type <code>ExtractOnProp</code> that extracts an object from a union type <code>T</code> whose specific property <code>Prop</code> has a requested type <code>ValueType</code>.</p>
  <pre class="lang-js prettyprint-override"><code>type ExtractOnProp&lt;T, Prop extends keyof T, ValueType&gt; = /* ... */;
  
  type Test1 = ExtractOnProp&lt;
    | { value: &quot;test1&quot; | &quot;test2&quot;, prop1: any }
    | { value: &quot;test1&quot;, prop2: any }
    | { value: &quot;test3&quot;, prop3: any },
    &quot;value&quot;,
    &quot;test1&quot;&gt;;
  // Test1 == { value: &quot;test1&quot;, prop1: any } | { value: &quot;test1&quot;, prop2: any };
  
  // Real use case is more complicated but example usage:
  // We want to find an element from an array of possible values
  function narrow&lt;
    T extends { value: string },
    Value extends readonly T['value'][],
  &gt;(
    elements: readonly T[],
    filter: Value,
  ): ExtractOnProp&lt;T, 'value', Value[number]&gt; {
    const result = elements.find((el) =&gt; filter.includes(el.value as any));
    if (!result) {
      throw new Error('not found');
    }
    return result as any;
  }
  narrow(
    [
      { value: 'test1', prop1: 1 },
      { value: 'test2', prop1: 2 },
      { value: 'test3', prop3: 3 },
    ] as const,
    ['test1', 'test3'] as const,
  );
  // { value: 'test1', prop1: 1 } | { value: 'test3', prop3: 3 }
  </code></pre>
  <p>Here are the things I've tried</p>
  <pre class="lang-js prettyprint-override"><code>type ExtractOnProp&lt;T, Prop extends keyof T, ValueType&gt; = T[Prop] extends ValueType ? T : never;
  // Always returns never
  
  // The closest I got was property type intersection
  type IntersectPropType&lt;T, Prop extends keyof T, ValueType&gt; = T[Prop] extends infer S
    ? S extends ValueProp
      ? S
      : never
    : never;
  type Test1 = IntersectPropType&lt;{ value: 'test1' | 'test2' } | { value: &quot;test3&quot;; prop: any }, 'value', 'test2' | 'omitted'&gt;;
  // Test1 = 'test2';
  </code></pre>
  <p><strong>Edit:</strong></p>
  <p>I've gotten closer now by narrowing the union, but not the property type itself.</p>
  <pre class="lang-js prettyprint-override"><code>type ExtractOnProp&lt;T, Prop extends keyof T, ValueType&gt; = T extends {
    [_ in Prop]: infer S;
  }
    ? ValueType extends S
      ? T
      : never
    : never;
  
  type Test = ExtractOnProp&lt;
    { value: 'a' | 'c'; prop1: any } | { value: 'b'; prop2: any },
    'value',
    'a'
  &gt;;
  // Test = { value: 'a' | 'c', prop1: any }
  </code></pre>