skube
skube

Reputation: 6175

Element implicitly has an 'any' type because index expression is not of type 'number' [7015]

I've taken the code from David Walsh's css animation callback and modified it to TypeScript. However, I'm getting an error and I don't know why:

interface IBrowserPrefix {
  [key: string]: string;
}

// http://davidwalsh.name/css-animation-callback
function whichAnimationEvent() {
  let x: keyof IBrowserPrefix;
  const el = document.createElement('temp');
  const browserPrefix: IBrowserPrefix = {
    animation: 'animationend',
    OAnimation: 'oAnimationEnd',
    MozAnimation: 'animationend',
    WebkitAnimation: 'webkitAnimationEnd',
  };

  for (x in browserPrefix) {
    if (el.style[x] !== undefined) {
    //           ^---- [TS Error]: Element has 'any' type b/c index expression is not of type 'number'
      return browserPrefix[x];
    }
  }
}

Upvotes: 12

Views: 44558

Answers (3)

Dan
Dan

Reputation: 10538

This is happening because you're attempting to index an object with a numeric index signature with string keys.

for x in browserPrefix will give you back a set of keys, which are strings. However for some reason CSSStyleDeclaration has its index type set to number (and not string) - see https://github.com/Microsoft/TypeScript/issues/17827.

You're getting this error because you have --noImplicitAny turned on. A way to get this working (a hacky way) would be to cast the indexer to a string:

  for (x in browserPrefix) {
    if (el.style[x as any] !== undefined) {
      return browserPrefix[x];
    }
  }

The other way would be to modify the typings (try bumping the issue on github).

while we're here, you should mark x with const and if you're going to use for-in on an object you should make sure that the property belongs to the object to avoid pulling in anything that is inherited in the prototype chain:

  for (const x in browserPrefix) {
    if (browserPrefix.hasOwnProperty(x) && el.style[x as any] !== undefined) {
      return browserPrefix[x];
    }
  }

Alternatively, use for-of with Object.keys instead of for-in.

There's no need to define x ahead of time here.

Upvotes: 13

Charles Stover
Charles Stover

Reputation: 1148

Try for (x of Object.keys(browserPrefix)) instead of for (x in browserPrefix).

It's typically frowned upon to use the in keyword for a loop, because you may get properties that do not belong to the object.

Upvotes: 2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249506

There are several problems in the code, the first one is that IBrowserPrefix is defined as having a string index and thus keyof IBrowserPrefix; will actually be string. I would remove the interface and just use let x: keyof typeof browserPrefix;

The next problem is the way typescript defined the CSSStyleDeclaration interface. It only include standard properties, not vendor specific ones.

You could a type assertion to tell the compiler you know what you are doing and ignore the error

export function whichAnimationEvent() {

    const el = document.createElement('temp');
    const browserPrefix = {
        animation: 'animationend',
        OAnimation: 'oAnimationEnd',
        MozAnimation: 'animationend',
        WebkitAnimation: 'webkitAnimationEnd',
    };
    let x: keyof typeof browserPrefix;
    for (x in browserPrefix) {
        if (el.style[x as keyof CSSStyleDeclaration] !== undefined) {
            return browserPrefix[x];
        }
    }
}

You could also extend with CSSStyleDeclaration with the vendor specific keys you require.

Upvotes: 3

Related Questions