21

I'm trying to check whether a variable belongs to a certain type or not.

Code:

type GeneralType = SubTypeA | SubTypeB;
type SubTypeA = 'type1' | 'type2';
type SubTypeB = 'type3' | 'type4';

function someFunction(arg1: GeneralType) {
  if (arg1 instanceof SubTypeA) {
    // Do something
  }
  // Continue function
  return arg1;
}

Of course this code fails in line 6 because instanceof is not usable for types. Is there an alternative option I could use without needing to check explicitly every posible value for SubTypeA?

Martín De la Fuente
  • 6,155
  • 4
  • 27
  • 28
  • 1
    Maybe [this answer](https://stackoverflow.com/a/51529486/5048820) is useful – Marian Simonca Jan 11 '19 at 13:41
  • typescript does not exists in runetime. You can use class insteed of type. but type is pure ts. No way, this is not possible. But you can create type with some discriminant and then use switch case on this discriminant and typescript will be able to narrow the current type – Juraj Kocan Jan 11 '19 at 14:43

2 Answers2

5

As mentioned in the comments, it seems there's no strightforward method to achieve this.

Finally, the most elegant way I found to do it is using Type Guards as follows:

type GeneralType = SubTypeA | SubTypeB;
type SubTypeA = 'type1' | 'type2';
type SubTypeB = 'type3' | 'type4';

function someFunction(arg1: GeneralType) {
  if (isSubTypeA(arg1)) {
    // Do something
  }
  // Continue function
}

function isSubTypeA(arg: GeneralType): arg is SubTypeA {
  return ['type1', 'type2'].some(element => element === arg);
}

A more detailed explanation can be found here.

Martín De la Fuente
  • 6,155
  • 4
  • 27
  • 28
  • 7
    I wish there were a more elegant, less verbose way to do this, such as assert the type in the if statement without the helper function. – 55 Cancri Apr 12 '20 at 12:58
2

Type guards are commonly used to discriminate unions. There are more ways this can be done:

  1. Use the switch statement.
  2. Use enums.

The switch statement

This method is easy but can become cumbersome if your unions are big.

function someFunction(arg1: GeneralType) {
  switch(arg1) {
      case 'type1':
      case 'type2':
        return /* */  
      default:
        /* ... */
  }
}

someFunction('type1');

Enums

The disadvantage here is that it doesn't work with string enums, only the regular ones.

enum SubTypeA {
    type1,
    type2,
}

enum SubTypeB {
    type3,
    type4,
}

type GeneralType = SubTypeA | SubTypeB;

function someFunction(arg1: GeneralType) {
    if (arg1 in SubTypeA) {
        /* ... */
    }
}

someFunction(SubTypeA.Type1);

As you can see, writing a type guard requires more work up front, but type guards don't have the limitations other methods have. In addition to that, they are just functions, so they can be reused. You made a good choice.

Karol Majewski
  • 23,596
  • 8
  • 44
  • 53