sindresorhus / ow

Function argument validation for humans
https://sindresorhus.com/ow/
MIT License
3.8k stars 105 forks source link

Composition #233

Open ziflex opened 2 years ago

ziflex commented 2 years ago

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.

sindresorhus commented 2 years ago

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

sindresorhus commented 2 years ago

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,
}))
ziflex commented 2 years ago

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: