sindresorhus/ow

Composition

ziflex opened this issue · 3 comments

Hi guys, thanks for this amazing library!

I wonder if there is a way how to extend predicates or compose a new one using a set of others.
Here is a use case:

interface User {
    id: string;
    email: string;
}

const assertUser = ow.create('User', ow.object.partialShape({
    id: ow.string.nonEmpty,
    email: ow.string.nonEmpty,
}))

interface Person extends User {
    firstName: string;
    lastName: string;
}

const assertPerson = ow.create('Person', ow.object.partialShape({
    id: ow.string.nonEmpty,
    email: ow.string.nonEmpty,
    firstName: ow.string.nonEmpty,
    lastName: ow.string.nonEmpty,
}))

I would love to be able to avoid code duplication.

It might be possible, but it's not an easy task, especially because of TypeScript.

I think the easiest path for reuse is this:

interface User {
    id: string;
    email: string;
}

const owUser = {
    id: ow.string.nonEmpty,
    email: ow.string.nonEmpty,
};

const assertUser = ow.create('User', ow.object.partialShape(owUser))

interface Person extends User {
    firstName: string;
    lastName: string;
}

const assertPerson = ow.create('Person', ow.object.partialShape({
    ...owUser,
    firstName: ow.string.nonEmpty,
    lastName: ow.string.nonEmpty,
}))

That's what I ended up doing, but it's rather hacky and breaks an abstraction.

The easiest way of supporting this is to add a method something like ow.compose(name: string, ...validation: ReusableValidator<any>[]).
But it creates 2 problems:

  • TypeScript unfriendliness
  • Error messages will point to different objects