Repository: TypeScript
Issue: Inconsistent type compatibility for a type with a call signature and and index signature
Labels: Suggestion,Awaiting More Feedback


<!-- Please try to reproduce the issue with `typescript@next`. It may have already been fixed. -->
**TypeScript Version:** 2.9.0-dev

<!-- Search terms you tried before logging this (so others can find this issue more easily) -->
**Search Terms:** index signature intersection

**Code**

```ts
type INOk = {
    (): string;
    [name: string] : number;
}

type IOk = {
    (): string;
} & {
    [name: string] : number;
}

declare let val:(() => "") & { foo: number; }
let ok: IOk = val; // This works
let nok: INOk = val; // This does not

```

**Expected behavior:**
Both `IOk` and `INOk` have the same public structure, they both have a call signature and are indexable, both assign statements should be valid.

**Actual behavior:**
The second assign statement fails with the message `Index signature is missing in type '(() => "") & { foo: number; }'.`

**Playground Link:** [link](https://www.typescriptlang.org/play/index.html#src=type%20INOk%20%3D%20%7B%0A%20%20%20%20()%3A%20string%3B%0A%20%20%20%20%5Bname%3A%20string%5D%20%3A%20number%3B%0A%7D%0A%0Atype%20IOk%20%3D%20%7B%0A%20%20%20%20()%3A%20string%3B%0A%7D%20%26%20%7B%0A%20%20%20%20%5Bname%3A%20string%5D%20%3A%20number%3B%0A%7D%0A%0Adeclare%20let%20val%3A(()%20%3D%3E%20%22%22)%20%26%20%7B%20foo%3A%20number%3B%20%7D%0Alet%20ok%3A%20IOk%20%3D%20val%3B%20%2F%2F%20This%20works%0Alet%20nok%3A%20INOk%20%3D%20val%3B%20%2F%2F%20This%20does%20not%0A)
**Related Issues:** https://github.com/Microsoft/TypeScript/issues/15300

**Notes**
Looking at the checker code, it appears that for `IOk` type compatibility is checked for each constituent of the intersection type, so we will have :

```ts
isRelatedTo (typeof val, IOk) = 
      isRealtedTo(typeof val, () => ""))   // == True, since val has a call signature
             ( // Above equal to 
                   isRelatedTo(() => "", () => "") // ==  True
                   ||
                   isRelatedTo({ foo: number; }, () => "") // == False but it's does not matter
             )
      &&
      isRealtedTo(typeof val, { [name: string] : number }) // = True since the { foo: number} part of typeof val has an inferable index (ie isObjectTypeWithInferableIndex( { foo: number} ) will return true)
            ( // Above equal to 
                   isRelatedTo(() => "",  { [name: string] : number }) // ==  False but does not matter
                   ||
                   isRelatedTo({ foo: number; },  { [name: string] : number }) // == True, 
             )

```

While for `INOk` the relation is checked directly, since `INOk` can't be split into constituents and we have 
```ts
isRelatedTo (typeof val, INOk) = 
              isRelatedTo(() => "",  INOk) // ==  False INOk has index, but ()=> "" does not
              ||
              isRelatedTo({ foo: number; },  INOk) // == False, No compatible call signature
```
So then the checker falls back to structural checking (`recursiveTypeRelatedTo`) but this fails as well because when checking for index compatibility (inside `indexTypesRelatedTo`), it decides that the `typeof value` (`(() => "") & { foo: number; }`)  does not have an inferable index (`isObjectTypeWithInferableIndex`  returns false because the intersection type does not have a `symbol` and even if it did the condition for inferable index checks that the type does not have a call signature (`!typeHasCallOrConstructSignatures(type)`))