4

I'm using Typescript 3.2.2 and unable to achieve the most basic implementation of type guards:

interface Bird {
    fly(): boolean;
}

interface Fish {
    swim(): boolean;
}

function swimIfYouCan(x: Fish | Bird) {
    if ((<Fish>x).swim) {
        return true
    }
}

This produces Error:(50, 11) TS2693: 'Fish' only refers to a type, but is being used as a value here. in WebStorm and SyntaxError: /Users/sea-kent/git/docket-management/webapp/src/App.tsx: Unexpected token (51:8) from yarn. Is there a configuration needed to enable this syntax?

Aaron Kent
  • 609
  • 1
  • 5
  • 11
  • 3
    If you're using TSX, `` could be an element - use `x as Fish` instead. See e.g. https://stackoverflow.com/a/54614279/3001761, https://www.typescriptlang.org/docs/handbook/jsx.html – jonrsharpe Feb 11 '19 at 15:50
  • @jonrsharpe that was it thank you – Aaron Kent Feb 11 '19 at 16:00
  • Related: [Why does 'instanceof' in TypeScript give me the error “'Foo' only refers to a type, but is being used as a value here.”?](https://stackoverflow.com/questions/46703364/why-does-instanceof-in-typescript-give-me-the-error-foo-only-refers-to-a-ty) – user247702 Feb 11 '19 at 16:18

2 Answers2

2

There are three different approaches possible that come to my mind for your problem.

  1. If you want to cast it and directly use it you have to use the as operator (as mentioned by @jonrsharpe in the comments), so for example (x as Fish).swim

  2. use the in operator to check if it is available, e.g. if ('swim' in x)

  3. Last but not least (and for this case in my opinion the best case), simply do an instance of check to check what instance you've got: if (x instance of Fish)

Edit: Didn't read the question well enough, you can't check for interfaces in runtime. But there is no harm in creating a class on top of the interface

Nicolas Gehlert
  • 2,626
  • 18
  • 39
  • 2
    The last will not work, since interfaces don't exist in runtime. – Cerberus Feb 11 '19 at 16:06
  • 2
    `x instanceof Fish` gives the same problem `'Fish' only refers to a type, but is being used as a value here.` – Aaron Kent Feb 11 '19 at 16:10
  • 1
    `in` works fine if I just want to call the method, but if I want to have a `Fish` to pass to something else that is checking, it won't do. I think `x as Fish` is the winner. Thanks. – Aaron Kent Feb 11 '19 at 16:10
  • 1
    yeah didn't read question proper enough that he is using an interface and not a class – Nicolas Gehlert Feb 11 '19 at 16:14
1

The recommended way to do pattern matching by checking the existence of a property is the in operator:

interface Bird {
    fly(): boolean;
}

interface Fish {
    swim(): boolean;
}

function move(x: Fish | Bird) {
  if ("swim" in x) {
    return x.swim();
  } else {
    return x.fly();
  }
}

By using the in operator you can avoid the assertion.

Remo H. Jansen
  • 23,172
  • 11
  • 70
  • 93
  • Thanks. That works for this trivial case, but if I want to go a step further and actually have a `Fish` to pass to something else, I think `x as Fish` is more inline with the goal: ```function isFish(x: Fish | Bird): x is Fish { return (x as Fish).swim !== undefined; }``` – Aaron Kent Feb 11 '19 at 16:13