Learning TypeScript 2.x
上QQ阅读APP看书,第一时间看更新

Custom type guards

We can define custom type guards using by declaring a function with a special return type:

interface Supplier {
orderItems(): void;
getAddress(): void;
}

interface Customer {
sellItems(): void;
getAddress(): void;
}

function isSupplier(person: Supplier | Customer): person is Supplier {
return (<Supplier> person).orderItems !== undefined;
}

function handleItems(person: Supplier | Customer) {
if (isSupplier(person)) {
person.orderItems(); // OK
} else {
person.sellItems(); // OK
}
}

The preceding code snippet declares two types (Supplier and Customer); it then declares a custom type guard function. The custom type guard returns a Boolean value. The function returns true when the provided value person has a property named orderItems and false when the property is missing.

The function is trying to identify the type at runtime by examining the properties of the value. This kind of type matching is known as pattern matching.

We will learn more about pattern matching in Chapter 7, Functional Programming with TypeScript.

Pattern matching is not the only technique that we can use to identify if a value matches a type. We can also use the instanceof operator:

class Supplier {
public orderItems(): void {
// do something...
}
public getAddress(): void {
// do something...
}
}


class Customer {
public sellItems(): void {
// do something...
}
public getAddress(): void {
// do something...
}
}


function isSupplier(person: Supplier | Customer): person is Supplier {
return person instanceof Supplier;
}


function handleItems(person: Supplier | Customer) {
if (isSupplier(person)) {
person.orderItems(); // OK
} else {
person.sellItems(); // OK
}
}

Another technique that we can use to identify if a value matches a type is to use the typeof operator:

function doSomething(x: number | string) {
if (typeof x === 'string') {
console.log(x.subtr(1)); // Error
console.log(x.substr(1)); // OK
}
x.substr(1); // Error
}

The preceding code snippet throws a compilation error within the if block because TypeScript knows that the variable x must be a string within the block. Another error is thrown outside of the if block, because TypeScript cannot guarantee that the type of the variable x is string at that point.

Since TypeScript 2.7, we can use the in operator as a type guard to narrow down a given type, as demonstrated by the following example:

interface Cat {
meow(): void;
}


interface Dog {
woof(): void;
}


function doSomething(obj: Cat | Dog) {
if ("meow" in obj) {
obj.meow(); // OK
} else {
obj.woof(); // OK
}
}