Thats awesome, have been thinking of this for a while of doing it my self but never found the time to work it through. What you could do is make the option types definitions be generated automatically such that you don't have to define all the functions each time.
optionsTypes[fn_Symbol, options___] := Block[{},
(checkOption[fn, #] := True) & /@ Flatten[List[options]]
]
Then you cant make the function definition easier by using
Options[myFunction] = {
"anInteger" -> 1,
"aReal" -> 1.0,
"anAssociation" -> <||>
};
optionsTypes[myFunction,
"anInteger" -> _Integer,
"aReal" -> _?NumericQ,
"anAssociation" -> _Association
]
This will just add the definitions to the checkOption function.
In[74]:= Definition[checkOption]
checkOption[myFunction,anInteger->_Integer]:=True
checkOption[myFunction,aReal->_?NumericQ]:=True
checkOption[myFunction,anAssociation->_Association]:=True
checkOption[fn_Symbol,opt_->value_]:=Module[{},Message[checkOption::invldopt,fn,opt,value];False]
Thinking of it a bit more one could also not care about some option values so they don't need to be defined and will therefore also not need to generate an error.
optionsTypes[fn_Symbol, options___] := Block[{opts, all, undefined},
opts = Flatten[List[options]];
all = Options[fn];
undefined = Complement[all[[All, 1]], opts[[All, 1]]];
(checkOption[fn, #] := True) & /@ opts;
(checkOption[fn, # -> _] := True) & /@ undefined;
]
This way this will not generate errors
Options[myFunction] = {
"anInteger" -> 1,
"aReal" -> 1.0,
"anAssociation" -> <||>,
"dontCare" -> 123
};
optionsTypes[myFunction,
"anInteger" -> _Integer,
"aReal" -> _?NumericQ,
"anAssociation" -> _Association
]
{myFunction[3, 2],
myFunction[3, 2, "anInteger" -> 5],
myFunction[3, 2, "aReal" -> 2.5],
myFunction[3, 2, "anInteger" -> 5, "aReal" -> 2.5],
myFunction[1, 3, "anInteger" -> 2, "anAssociation" -> <|"a" -> "b"|>],
myFunction[1, 3, "anInteger" -> 2, "anAssociation" -> <|"a" -> "b"|>,
"dontCare" -> 32]
}