Search code examples
typescriptfiltertypescastingtypeguards

How to type guard TypeScript type if instanceof only supports classes?


Need to type guard but instanceof doesn't work with TypeScript types:

type Letter = 'A' | 'B';
const isLetter = (c: any): c is Letter => c instanceof Letter; // Error: 'Letter' only refers to a type, but is being used as a value here.

// Expected usage: Filter via type guard.
isLetter('a'); // Should output true
'foo bar'.split('').filter(c => isLetter(c)); // Should output 'a'

Haven't found similar questions but instanceof works with classes:

class Car {}
const isCar = (c: any): c is Car => c instanceof Car; // No error
isCar('a'); // false

If instanceof only works with classes, what is it's equivalent for type, & how can we type guard using a TypeScript type?


Solution

  • TS types only exist in the compilation phase, not in runtime.

    In a user-defined type guard it is your duty to implement appropriate checks. The check will vary depending on the type of input and output - selecting between a few alternatives is simpler than asserting the shape of completely unknown object.

    For your type Letter (union of 'A' and 'B') it is enough to check whether input is A or input is B

    const isLetter = (c: any): c is Letter => c == 'A' || c == 'B';
    

    If your union has more members and you dont want to repeat them in union and in type guard:

    const letters = ['A', 'B'] as const;
    type Letter = typeof letters[number];
    const isLetter = (c: any): c is Letter => letters.includes(c);
    

    Playground link

    Note: Classes are preserved at runtime (via prototype chain) - which is why you can use instanceof operator with classes.