16

Is it possible to create a typeguard, or something else that accomplishes the same purpose, to check if a variable is a specific interface type in a typescript union?

interface Foo { a:string }
interface Bar { b:string }

(function() {
    function doStuff(thing: Foo | Bar) {
        if(typeof thing === 'Foo') {
            console.log('Foo');
        } 
        else if (typeof thing === 'Bar') {
            console.log('Bar');
        }
        else {
            console.log('unknown');
        }
    }

    var thing: Foo = {a:'a'};
    doStuff(thing);
})();

3 Answers 3

24

Since Typescript 1.6 you can use user-defined type guards:

let isFoo = (object: Foo| Bar): object is Foo => {
    return "a" in object;
}

See https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards and https://github.com/microsoft/TypeScript/issues/10485

4
  • 1
    This is the best answer! Commented Dec 19, 2017 at 6:26
  • 2
    The big one answer here is that 'key' in object is understood by TypeScript as a way to tell interfaces apart. Sadly typeof object.key === 'undefined' fails in this case.
    – fregante
    Commented Jun 23, 2019 at 11:53
  • 3
    A problem with this approach is that if a is renamed to aa, the code will still compile, even if a is no longer a discriminator for the Foo interface. Actually, it might be better not to use a type guard and just do an inline member check with in. Then the compiler will infer the type - as long as a is part of Foo. Anyone got a more type safe approach to this? Commented Aug 26, 2020 at 4:47
  • @JørgenTvedt That's the main problem indeed. I'm very interested by a solution to this.
    – Fl4v
    Commented Aug 26, 2020 at 14:40
9

typeof doesn't do this. It always return "string", "number", "boolean", "object", "function", or "undefined".

You can test for object properties with a test like if(thing.a !== undefined) { or if(thing.hasOwnProperty('a')) {.

Note that you could make an object that had both a string a and a string b, so be aware of that possibility.

1
  • 4
    So there's no way to check if a variable conforms to an interface without checking specific properties (like 'a' in our example)?
    – myartsev
    Commented Mar 20, 2015 at 17:34
3

In TypeScript 2 you can use Discriminated Unions like this:

interface Foo {
    kind: "foo";
    a:string;
}
interface Bar {
    kind: "bar";
    b:string;
}
type FooBar = Foo | Bar;
let thing: FooBar;

and then test object using if (thing.kind === "foo").

If you only have 2 fields like in the example I would probably go with combined interface as @ryan-cavanaugh mentioned and make both properties optional:

interface FooBar {
    a?: string;
    b?: string
}

Note that in original example testing the object using if (thing.a !== undefined) produces error Property 'a' does not exist on type 'Foo | Bar'.

And testing it using if (thing.hasOwnProperty('a')) doesn't narrow type to Foo inside the if statement.

@ryan-cavanaugh is there a better way in TypesScript 2.0 or 2.1?

1
  • This is the safest way to get compiler checks on unions, IMO, and the best way to handle OP's problem. I think this is the best answer now @myartsev
    – jocull
    Commented May 27, 2017 at 10:49

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.