Typescript: Incorrect inference when combining intersection types and index signatures

Created on 20 Sep 2017  ·  6Comments  ·  Source: microsoft/TypeScript



TypeScript Version: 2.4.0 / nightly (2.5.0-dev.201xxxxx)

Code

type A = {
 [index: string]: string;
} & {
 body: string | A;
}

var x: A;

var a = x.hi;
var b = x.body; // var b: string | A

let c:A = { body: { body: 'nope'} } // TypeError: property 'body' is incompatible with index signature

Expected behavior:
b should be inferred to be of type string since the index type forces every property of x to be string
Actual behavior:
b is inferred to a more general type that can actually be assigned to it

Working as Intended

Most helpful comment

The actual & expected behaviour section here implies this should be made consistent by being _more_ strict, another option (that I personally would prefer) would be to make it consistent by being less strict instead.

I.e. x.body should still be inferred as string | A, but it should also allow you to provide a body property of type A.

All 6 comments

The actual & expected behaviour section here implies this should be made consistent by being _more_ strict, another option (that I personally would prefer) would be to make it consistent by being less strict instead.

I.e. x.body should still be inferred as string | A, but it should also allow you to provide a body property of type A.

@mhegazy sorry to insist, but I don't understand why this is working as intended. I came up with a similar example that maybe highlights the problem better.

type A = {
 [index: string]: string;
} & {
 body: string | A;
}

var b: A;
var c: A; 

b = { body: '' }
c = { body: '' }

c.body = b // This works
c = { body: b } // TypeError

Expected behavior
I would expect the two constructions to be either both allowed or both disallowed.
Actual behavior
You can construct c one way but not the other.

At the point that you construct a self-contradictory type A, I don't know how to determine what behavior is "expected" and what's not.

I also encountered this problem. I tried to intersect two index signatures and I thought it would unionise the types.

interface A {
  a: string;
};

interface B {
  b: string;
}

let ab: A & B = {
  a: '',
  b: '',
} // Working as intended

interface String {
  [index: string]: string;
}

interface Numbers {
  [index: string]: number;
}

let sn: String & Numbers = {
  a: '',
  b: 1,
} // Error

http://www.typescriptlang.org/play/#src=%0Ainterface%20String%20%7B%0A%20%20%5Bindex%3A%20string%5D%3A%20string%3B%0A%7D%0A%0Ainterface%20Numbers%20%7B%0A%20%20%5Bindex%3A%20string%5D%3A%20number%3B%0A%7D%0A%0Alet%20sn%3A%20String%20%26%20Numbers%20%3D%20%7B%0A%20%20a%3A%20''%2C%0A%20%20b%3A%201%2C%0A%7D%0A%0Ainterface%20A%20%7B%0A%20%20a%3A%20string%3B%0A%7D%3B%0A%0Ainterface%20B%20%7B%0A%20%20b%3A%20string%3B%0A%7D%0A%0Alet%20ab%3A%20A%20%26%20B%20%3D%20%7B%0A%20%20a%3A%20''%2C%0A%20%20b%3A%20''%2C%0A%7D

My expectation is String & Numbers should resolve to:

interface StringsNumbers {
    [index: string]: number | string;
}

I don't think this is working as intended, since removing one property to satisfy the error doesn't resolve the issue. It will just complain that the other property doesn't satisfy the other index signature. Intersecting two index signatures is effectively only accepting {} in this case.

One more unusual thing I encountered:

interface Strings {
  [index: string]: string;
}

interface Numbers {
  [index: string]: number;
}

interface A {
  a: string;
}

function f(ab: Strings & Numbers & A) {
  ab.a; // No error
}

f({ a: '' }); // Error

I treats the type differently inside the function versus in argument location.

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

kyasbal-1994 picture kyasbal-1994  ·  3Comments

siddjain picture siddjain  ·  3Comments