Flow: Casting to a subtype / force cast

Created on 6 Jun 2016  路  12Comments  路  Source: facebook/flow

When the built-in declarations are incorrect and/or missing functions, it would be really helpful to be able to force a cast. For example:

let event = document.createEvent('MouseEvents');
event.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
foo(event);

function foo(e: MouseEvent) {
  //...
}

This throws the errors:

$ flow check-contents < foo.js
-:1
  1: let event = document.createEvent('MouseEvents');
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `createEvent`
  3: foo(event);
         ^^^^^ Event. This type is incompatible with
  5: function foo(e: MouseEvent) {
                     ^^^^^^^^^^ MouseEvent

-:2
  2: event.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
           ^^^^^^^^^^^^^^ property `initMouseEvent`. Property not found in
  2: event.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
     ^^^^^ Event

I need a way to cast Event to MouseEvent, as I know (but Flow does not) that the return type of document.createEvent('MouseEvents') is MouseEvent.

This is also related to #396 as this would be fixable with an easy way to extend built-in types.

Most helpful comment

Try this:

function foo(e_: any) {
  const e: MouseEvent = e_;
}

In general you can use this pattern for force-casting:

const foo: number = (('foo': any): number)

All 12 comments

Try this:

function foo(e_: any) {
  const e: MouseEvent = e_;
}

In general you can use this pattern for force-casting:

const foo: number = (('foo': any): number)

That's pretty brutal, but the second option works. We really could use some official syntax for this.

I run into this all the time, it would be nice if there was something like (e: $Cast<MouseEvent>) => {} which could just be a short hand for @vkurchatkin's workaround.

Yeah, this would be hugely useful for e.g. screwing with e.target, which 99% of the time is a Node, not just an EventTarget.

IMO it's a good thing that it looks ugly and stands out

$DangerouslyUnsoundCast<MouseEvent> :smile:

@vkurchatkin I see where you're coming from but it's so common, especially when dealing with React that it does warrant a suitably scarily named shortcut IMHO. In my experience people just use any without recasting when confronted with this kind of issue, and that's worse.

Well, maybe. For me any is scary enough. Probably it's worth improving coverage command and encouraging people to use it

Swift has a beautiful syntax for the case where you just want to assume a value is non-null, using a bang to 'unwrap' an unsafe value, like myObj!.

Like if you have a getMenuItem(key:string): ?Object method you can call,
const sports:Object = this.getMenuItem(MenuKeys.SPORTS)!

This lends itself to much shorter inline code like this.getMenuItem(MenuKeys.SPORTS)!.selectable = true.

The bang syntax indicates the circumvention of type-safety in specific cases where you are positive a value will exist so it's naturally self-documenting.

Personally I'd love a feature like that in Flow since currently I'm writing big null checks after getting values I am certain will not return null.

I use a function like this:

function unwrap<T>(val: ?T): T {
  if (val == undefined) throw new Error();
  return val;
}

Closing since the initial issue has been resolved. You can cast through any 馃憤

The correct solution is to fix our libdefs. We鈥檇 be happy to accept PRs 馃槉

I agree with @mosesoak 100%. Why not make maybe-unwrapping more concise? 馃槙

Was this page helpful?
0 / 5 - 0 ratings

Related issues

funtaps picture funtaps  路  3Comments

mmollaverdi picture mmollaverdi  路  3Comments

ctrlplusb picture ctrlplusb  路  3Comments

iamchenxin picture iamchenxin  路  3Comments

ghost picture ghost  路  3Comments