6

I'm liking typescript so far, but find that i need to do type assertion a lot. For example casting an EventTarget to an HTMLAnchorElement is a common use case for me. However to get that, i need to use something like the following:

getTabID(eventTarget: EventTarget) : string {
    // without the following variable, the compiler tells me that .hash 
    // is not a property of EventTarget, which according to the interface, it isn't.
    // So thats fine, i'll cast it to an Element
    let mEventTarget: HTMLAnchorElement = <HTMLAnchorElement>eventTarget
    let mTabID: string
    if(mEventTarget.hash){
        mTabID = mEventTarget.hash.split('#')[1]
    } 
    return mTabID
}

However this means that if I don't want the compiler to throw errors I need to create variables in my functions JUST to do type assertions. I don't mind the extra typing, but these end up in the JS as well and ends up wasting bytes in my js files.

I would like to be able to the following:

getTabID(eventTarget: EventTarget) : string {
    let mTabID: string
    // Do the type assertion in the parameter 
    if(<HTMLAnchorElement> eventTarget.hash){
        mTabID = mEventTarget.hash.split('#')[1]
    } else {
        mTabID = mEventTarget.dataset.tabId
    }
    return mTabID
}

I've had a good look in the docs and SO and can't seem to find any way to do this. Anyone have any ideas?

C02Equinox
  • 61
  • 1
  • 6
  • 2
    If you are sure that `eventTarget` will always be a `HTMLAnchorElement`, why not declare `eventTarget: HTMLAnchorElement` in the parameter and force callers of `getTabID` to ensure that it is `HTMLAnchorElement`? – Saravana Jun 07 '17 at 06:45
  • 1
    You could use `eventTarget: EventTarget | HTMLAnchorElement` as type, which enables both EventTarget and HTMLAnchorElement as types. – Spitzbueb Jun 07 '17 at 06:56
  • @Saravana because when I call that function the caller throws an error saying you can't pass an `EventTarget` to a function that expects an `HTMLAnchorElement`. @Wernerson I suppose that would stop the errors, but seems like a hack. – C02Equinox Jun 08 '17 at 07:05
  • 1
    @C02Equinox The point is you can do the assertion at the caller end. Like `getTabID(target as HTMLAnchorElement)`. If all this feels like a hassle, I would just type the parameter with `any`: `(eventTarget: any)` to turn off type checking. – Saravana Jun 08 '17 at 07:08
  • Oh that's brilliant! I had tried to use `as` syntax but couldn't figure out where to use it. Perfect! Thank you! – C02Equinox Jun 08 '17 at 07:21

3 Answers3

1

You can perform inline type assertions, by surrounding the assertion with parantheses:

if((<HTMLAnchorElement>eventTarget).hash) {

You might also see what you can do to prevent the need for a type assertion, for example:

getTabID(eventTarget: HTMLAnchorElement) : string {
    let mTabID: string;

    if(eventTarget.hash){
        mTabID = eventTarget.hash.split('#')[1]
    } 

    return mTabID
}

And lastly, watch out for the casing as you mixed EventTarget and eventTarget in one of your examples.

Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Your first solution is what I was hoping for, and seems to work brilliantly. Thanks! Your second solution however only shifts the problem from that function to the caller. calling `this.getTabID(event.target)` simply throws an error saying you can't assign EventTarget to type HTMLAnchorElement – C02Equinox Jun 08 '17 at 07:00
0

You can use type guards to implement this.

An example would be:

function isAnchor(eventTarget: EventTarget): eventTarget is HTMLAnchorElement {
  return (<HTMLAnchorElement>eventTarget).hash != undefined;
}


function getTabID(eventTarget: EventTarget): string {
  let mTabID: string
  // Do the type assertion in the parameter 
  if (isAnchor(eventTarget)) {
    mTabID = eventTarget.hash.split('#')[1]
  } else {
    mTabID = eventTarget.dataset.tabId
  }
  return mTabID
}

Note: I didn't know what interface dataset was part of but you can make a type guard for it as well.

You can learn more about type guards in the handbook here.

toskv
  • 30,680
  • 7
  • 72
  • 74
  • 1
    Thanks for the suggestion! That does satisfy the compiler, however it implements an entirely new function just to do type assertion. This probably reduces potential for code errors but does nothing to reduce file size. – C02Equinox Jun 08 '17 at 07:18
0
function getTabID(eventTarget: EventTarget) : string {
let mTabID: string

// Do the type assertion in the parameter 
if((<HTMLAnchorElement> eventTarget).hash){    // <==i add parentheses here 
    mTabID = mEventTarget.hash.split('#')[1]
} else {
    mTabID = mEventTarget.dataset.tabId
}

return mTabID
}
devzom
  • 676
  • 1
  • 9
  • 19
John Kb
  • 11
  • 1