ProdigySim
ProdigySim

Reputation: 2933

Create User Defined Type Guards for fields or properties automaticaly?

In making some code strict-mode compliant, I often run into a pattern like:

interface Item {
  foo?: Foo;
}
interface Foo {
  bar: string;
}

declare const items: Item[];

items.filter(item => !!item.foo).map(item => item.foo.bar); // ERROR! item.foo is possibly Undefined

This creates an error with --strictNullChecks, since the information that item.foo is defined does not get propagated.

In general, I've been creating one-off type guards to handle something like this.

function hasFoo(item: Item): item is Item & { foo: Foo } {
  return !!item.foo;
}

items.filter(hasFoo).map(item => item.foo.bar);

But that's an awful lot of custom code to define for every single type I use this pattern on. Is there some way to write a function that returns a custom type guard?

I would love to write something like:

items.filter(hasField('foo')).map(item => item.foo.bar);

Upvotes: 0

Views: 176

Answers (1)

ProdigySim
ProdigySim

Reputation: 2933

You can write a high-order function that returns a user-defined type guard. You might consider this a user-defined type guard factory.

function hasField<T, K extends keyof T>(key: K) {
    return (item: T): item is (T & Required<Pick<T, K>>) => {
        return typeof item[key] !== 'undefined';
    }
}

With usage like:

items.filter(hasField('foo')).map(items => items.foo.bar);

This will assert that the given field is available, and that it is not undefined.

TypeScript Playground

Upvotes: 1

Related Questions