Repository: TypeScript
Issue: Incorrect Indexed Return Types
Labels: Suggestion,Awaiting More Feedback

# Bug Report

### 🔎 Search Terms

indexer, return, type

### 🕗 Version & Regression Information

Reproduces on 4.8 as well as current 4.9 nightly.

### ⏯ Playground Link

- [Example 1](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGLIN4Chm+QbQGcAuZQsKUAcwF1TzKQqBuHPAcjndM-dYF8sWGAFcQCMMAD2IZDAA8AQWQQAHpBAATQmQrUAfAAo4pRQEpSqfIpqY2UCGBFRZ7AEZ8sgrAhnk5cMgAvHKGvGbMyAD0UQHIPFw+flIANhAAdClSVIbwEdGxAA6MYDoARG5lWEA)
- [Example 2](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGLIN4Chm+QbQGcAuZQsKUAcwF1TzKQqBuHPAcjndM-db2TsARt0EjWAXyxYYAVxAIwwAPYhkMADwBBZBAAekEABNCgrsgA+Y9gD4AFHFJaAlKVT4tNTGygQwsqDU4ZmQAelDkMEIEMjgAT1N3T2QAXmQQCAA3aCwpIA)
- [Example 3](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGLIN4Chm+QbQGcAuZQsKUAcwF1TzKQqBuHPAcjndPYCN3WeZH27CurAL5YsMAK4gEYYAHsQyGAB4AgsggAPSCAAmhMhWoA+ABRxSWgJSlU+LTUxsoEMLKhq4krCA)

### 💻 Code

#### Example 1, typechecks but should not
```ts
interface F {
    [s: string]: string;
    'a': 'a';
}

function f<A extends string>(a: A): F[A] {
  return 'b';
}

const fa = f('a'); // fa : 'a'
console.log(fa); // prints "b"
```

#### Example 2, does not typecheck but should

```ts
interface F {
    [s: string]: string;
    'a': 'a';
    'b': 'b';
}

function f<A extends 'a' | 'b'>(a: A): F[A] {
  return a; // tsc says F[A] = never
}
```

#### Example 3, typechecks but should not

```ts
interface F {
    [s: string]: string;
    'a': 'b';
    'b': 'a';
}

function f<A extends string>(a: A): F[A] {
  return a;
}
```

### 🙁 Actual behavior

tsc is calculating the wrong indexed return type for functions, leading to functions which should not typecheck but do and functions which do typecheck but should not. See examples above.

### 🙂 Expected behavior

tsc should correctly calculate the indexed return type for functions, at least conservatively so that functions which should not typecheck do not typecheck.

### Addendum

I think tsc is assuming the broadest type for `A` when calculating the return type. For Example 1, it assumes `A = string` and thus calculating `F[string] = string`, using the indexer definition. This follows for Example 2 by assuming `A = 'a' | 'b'` and thus calculating `F['a' | 'b'] = 'a' & 'b'` using the `'a'` and `'b'` properties. Again, Example 3 calculates like Example 1.

The mistake tsc is making is ignoring the conditionality of A.

In Example 1, it is not sufficiently to merely consider `A = string`; one must also consider `A = 'a'`. By so doing, you can calculate the return as `F[string] & F['a'] = string & 'a' = 'a'`, which is correct. However, it is also more conservative than necessary. More liberally, you can calculate if `A = string` then `F[A] = A` and if `A = 'a'` then `F[A] = A`, and thus the return type is `A` because `F[A] = A` for all cases.

In Example 2, the conservative calculation yields `string & 'a' & 'b' = string & never = never`. The liberal calculation yields `A` because again `F[A] = A` for all cases. The difference here is profound because the difference of calculation is determining whether `f` in Example 2 is implementable at all (other than by throwing or recursing).

In Example 3, the conservative calculation yields `string & 'b' & 'a' = never` and the liberal calculation yields `A & 'b' & 'a' = never`, so in this example they agree. Consider if the 'b' property was not present, then conservatively `string & 'b' = 'b'` and liberally `A & 'b'`. The liberal calculation is now unimplementable whereas the conservative calculation is!

I am not sure what the best solution is. I do think at least the conservative calculation is worth considering because it rejects the invalid programs.