TypeCheck: Fast and flexible runtime type-checking for your Elixir projects.
MIT License
config :app_name, :type_check [...]
to your configuration file(s). (c.f. #61)TypeCheck.External
module, with functions to work with typespecs in modules outside of your control. Thank you very much, @orsinium! (c.f. #113)
fetch_spec
to build a TypeCheck type from any function that has a @spec
.fetch_type
to build a TypeCheck type from any @type
.enforce_spec!
to wrap a call to any function that has a @spec
with a runtime type-check on the input parameters and return value.apply
and apply!
to wrap a call to any function with the function spec type that you give it.TypeCheck.Defstruct.defstruct!
, a way to combine defstruct
, @enforce_keys
and the creation of the struct's type, reducing boilerplate and the possibility of mistakes. (c.f. #118 )maybe_nonempty_list
type will no longer get stuck in an infinite loop on creation. (c.f. #120)Published by Qqwy over 2 years ago
Wooh, this is a big release!
%{required(key_type) => value_type}
Maps with a single kind of required key-value type.%{optional(key_type) => value_type}
Maps with a single kind of optional key-value type.%{:some => a(), :fixed => b(), :keys => c(), optional(atom()) => any()}
Maps with any number of fixed keys and a single optional key-value type.map(key, value)
has been changed to look the same as an optional map. This is a minor backwards-incompatible change.
%{}
has changed from 'any map' to 'the empty map' in line with Elixir's Typespecs. This is a minor backwards-incompatible change.
port()
, reference()
and (based on these) identifier()
.struct()
.timeout()
.nonempty_charlist()
and maybe_improper_list
and (based on these) iolist()
and iodata()
.TypeCheck.Credo.Check.Readability.Specs
: an opt-in alternative Credo check which will check whether all functions have either a @spec!
or 'normal' @spec
. (Fixes #102).TypeCheck.Builtin
module is now actually spectested itself. Some consistency bugs were found and solved as a result.Published by Qqwy over 2 years ago
Published by Qqwy almost 3 years ago
@type!
containing a %__MODULE__{}
is used before that module's defstruct
, a clear CompileError is created (c.f. #83).Published by Qqwy almost 3 years ago
def
/defp
/defmacro
/defmacrop
are always qualified(i.e. prefixed with 'Kernel.'), so they will also work in defprotocol
modules or other places where the normal versions of these macros are
hidden/overridden.
This fixes a bug reported on the Elixir forums by user 'belteconti'.
(c.f. PR #75 )
Published by Qqwy almost 3 years ago
Published by Qqwy about 3 years ago
Support for function-types (for typechecks as well as property-testing generators):
(-> result_type)
(...-> result_type)
(param_type, param2_type -> result_type)
Type-checking a function value against a function-type works a bit differently from most other types.
The reason for this is that we can only ascertain whether the function-value works correctly when the function-value is called.
Specifically:
TypeCheck.conforms/3
(and variants) or a function wrapped with a @spec!
is called, we can immediately check whether a particular parameter:
In other words, the 'wrapper function' which is added for a type (param_type, param_type2 -> result_type)
works similarly
to a named function with the spec @spec! myfunction(param_type, param_type2) :: result_type
.
As an example:
iex> # The following passes the first check...
iex> fun = TypeCheck.conforms!(&div/2, (integer(), integer() -> boolean()))
iex> # ... but once the function returns, the wrapper will raise
iex> fun.(20, 5)
** (TypeCheck.TypeError) The call to `#Function<...>/2` failed,
because the returned result does not adhere to the spec `boolean()`.
Rather, its value is: `4`.
Details:
The result of calling `#Function<...>.(20, 5)`
does not adhere to spec `(integer(), integer() -> boolean())`. Reason:
Returned result:
`4` is not a boolean.
This was quite the adventure to implement. I am happy that it turned out to be possible, and it is working great!
For property-testing generators, the data passed to a generated function is converted into a seed (using [param1, param2, param3] |> :erlang.term_to_binary |> Murmur.hash_x86_32
) and this seed is then used as seed for the data returned from the function.
This means that for any particular test run, any generated function will be pure (i.e. when given the same input multiple times, the same output will be returned).
__MODULE__
inside a struct inside a type now expands correctly. (c.f. #66)Published by Qqwy about 3 years ago
Support for bitstring literal types.
The types bitstring()
, binary()
and String.t()
were already supported.
However, support has now been added for the types:
<<>>
(matching an empty bitstring),<<_ :: size>>
(matching a bitstring of exactly size
bits long),<<_ :: _ * unit>>
(matching any bitstring of x * unit
where x :: pos_integer()
),<<_ :: size, _ :: _ * unit>>
(matching any bitstring of size + x * unit
where x :: pos_integer()
).Property-testing generators have also been constructed for them, so you can immediatly start using them with spectests.
Published by Qqwy about 3 years ago
Range.t()
. c.f. #58Thank you very much, @baldwindavid 💚 !
Published by Qqwy about 3 years ago
(This is the 'Rating' example from the Type-checking and spec-testing with TypeCheck article).
Types and values are pretty-printed in colour, similarly to how this is normally done in IEx.
use TypeCheck
now also calls require TypeCheck.Type
so there no longer is a need to call this manually if you want to e.g. use TypeCheck.Type.build/1
(which is rather common if you want to test out particular types quickly in IEx).Rating.t()
and String.t()
and only when looking at the problem in detail do we expand this to %Rating{}
and binary()
.[type]
no longer creates a fixed_list(type)
but instead a list(type)
(just as Elixir's own typespecs.)[...]
and [type, ...]
as alias for nonempty_list()
and nonempty_list(type)
respectively.TypeCheck.Builtin.Range
.Thank you very much, @baldwindavid and @0urobor0s !
Version 0.7.0 has been released! rocket
enable_runtime_checks
. When false, all runtime checks in the given module are completely disabled. This is useful to for instance disable checks in a particular environment. (c.f. #52) Thank you, @baldwindavid!DateTime.t
to the default overrides, as it was still missing.Published by Qqwy about 3 years ago
Its main changes are the addition of spectests and the implementation of a very large portion of the types in all modules of Elixir's standard library.
TypeCheck.ExUnit
, with the function spectest
to test function-specifications.
:except
, :only
, :initial_seed
.TypeCheck.DefaultOverrides
with many sub-modules containing checked typespecs for the types in Elixir's standard library (75% done).
TypeCheck.Option
.Enum.t
, Collectable.t
, String.t
.pid()
nonempty_list()
, nonempty_list(type)
.use TypeCheck
in IEx or other non-module contexts, to require TypeCheck
and import TypeCheck.Builtin
in the current scope (without importing/using the macros that only work at the module level.)__type_check__/1
is now added to any module that contains a use TypeCheck
.Inspect
implementation of custom structs, by falling back to Any
, which is more useful than attempting to use a customized implementation that would try to read the values in the struct and failing because the struct-type containing types in the fields.:stream_data
was not included in your project.