Alex Alex - 7 months ago 21
Javascript Question

SimpleSchema match any type but null

I'm planning to make a collection to hold different app-wide settings, like, say, amount of logged in users today, Google analytics tracking ID, etc. So I made a schema like this:

options_schema = new SimpleSchema({
key: {
type: String,
unique: true
},
value: {
},
modified: {
type: Date
}
});


Now the main problem is that I want
value
to be of any type: Number, String, Date, or even custom Objects. Though it has to be present, can't be
null
.

But of course it gets angry about not specifying the type. Is there a workaround for this?

Answer

You can use Match patterns for your fields' type which allow you to do pretty much anything :

const notNullPattern = Match.Where(val => val !== null)
value : {
  type : notNullPattern
}

(See Arrow functions)

Note that this will allow everything but null, including undefined.
Defining patterns this way allow you to use them everywhere in your application including in check :

check({
  key : 'the key',
  modified : Date.now(),
  value : {} // or [], 42, false, 'hello ground', ...
}, optionsSchema)
Match.test(undefined, notNullPattern) //true
Match.test({}, notNullPattern) //true
Match.test(null, notNullPattern) //false

A more general solution to exclude one value would simply be:

const notValuePattern =
  unwantedValue => Match.Where(val => val !== unwantedValue))

The use of which is similar to the above:

Match.test(42, notValuePattern(null)) // true

Note that due to the use of the identity operator === it will notably fail for NaN:

Match.test(NaN, notValuePattern(NaN)) // true :(

A solution could be:

const notValuePattern =
  unwantedValue => Match.Where(val => Number.isNaN(unwantedValue)?
    !Number.isNaN(val)
    : val !== unwantedValue
  )

Should you want a solution to exclude some specific values in a schema (kind of the contrary of Match.OneOf), you could use the following:

const notOneOfPattern = (...unwantedValues) => 
  Match.Where(val => !unwantedValues.includes(val)
)

This uses Array.prototype.includes and the ... spread operator. Use as follow:

Match.test(42, notOneOfPattern('self-conscious whale', 43)) // true
Match.test('tuna', notOneOfPattern('tyranny', 'tuna')) // false
Match.test('evil', notOneOfPattern('Plop', 'kittens')) // true

const disallowedValues = ['coffee', 'unicorns', 'bug-free software']
Match.test('bad thing', notOneOfPattern(...disallowedValues)) // true