Skip to content

Adding indexer to global Object makes other interfaces incompatible with Object #835

Closed

Description

@csnover

Between 0.9.1 and 0.9.5 the explicit indexer was removed from Object. Between 1.0 and 1.1, code that adds the indexer back to Object causes type comparison failures:

interface Object {
    [key:string]:any;
}

interface Foo {}

function a(b:Object) {}

var foo:Foo;
a(foo); // error TS2345: Argument of type 'Foo' is not assignable to parameter of type 'Object'.

Putting an indexer on Foo makes it work.

This is probably an OK error since adding back the indexer like this to Object should probably be considered an anti-pattern, and since in ES5 you can create inheritance patterns that don’t extend from Object, but it should at least be documented as a known breaking change.

Activity

  1. danquirk commented on Oct 6, 2014

    @danquirk
    Member

    I think this is just a bug and we aren't considering the indexer extensions to Object when checking the apparent type of foo.

  2. RyanCavanaugh commented on Oct 6, 2014

    @RyanCavanaugh
    Member

    This is seems to be according to the spec -- the augmented apparent type of Foo (3.8.1) does not include the index signatures of the Object type, only properties are added to the apparent type. It's possible this an oversight; following up.

  3. added
    By DesignDeprecated - use "Working as Intended" or "Design Limitation" instead
    and removed
    BugA bug in TypeScript
    on Oct 6, 2014
  4. RyanCavanaugh commented on Oct 6, 2014

    @RyanCavanaugh
    Member

    Confirmed by design. The ideal fix would be to replace b: Object with b: { } -- they're basically equivalent except for this deviation.

    We can talk about other fixes if you have more detailed use cases. Were you using the indexer to avoid noImplicitAny warnings?

  5. csnover commented on Oct 6, 2014

    @csnover
    ContributorAuthor

    Were you using the indexer to avoid noImplicitAny warnings?

    I suspect this is one of the reasons that it was used; I am still looking through to figure out and will update with more information as I have it.

  6. csnover commented on Oct 6, 2014

    @csnover
    ContributorAuthor

    Ryan Cavanaugh (@RyanCavanaugh) Here’s one pattern that adding the indexer to Object was “fixing”:

    interface Foo {
      // ...
    }
    
    function mixin(target:Foo, source:Foo):Foo {
      for (var key in source) {
        if (key === 'somethingNotToCopy') {
          continue;
        }
    
        // error TS7017: Index signature of object type implicitly has an 'any' type.
        target[key] = source[key];
      }
    
      return target;
    }

    1.0 would give TS7017 also until putting the indexer on Object; in 1.1 it doesn’t matter, it will complain anyway. Using explicit any type for the parameters, or coercing inside the method, would “solve” the problem, but I wouldn’t feel great about it.

  7. csnover commented on Oct 7, 2014

    @csnover
    ContributorAuthor

    Here are some other patterns triggering the new(ish) noImplicitAny behaviour:

    Feature detect something that isn’t in lib.d.ts:

    has.add('dom-pointerevents', Boolean(window['PointerEvent']));

    This one could use in, or it could do some interface merge.

    Late binding methods:

    function lateBind(target:Object, method:string):(...args:any[]) => any {
      return function ():any {
        return target[method].apply(target, arguments);
      };
    }

    This one would need to define target as any, which would fail at runtime if someone passed a non-object, like the first example.

    I think my remaining examples are all pretty much the same; foo[bar ? 'a' : 'b'], this[foo]…

  8. locked and limited conversation to collaborators on Jun 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    By DesignDeprecated - use "Working as Intended" or "Design Limitation" instead

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions