Releases: Qqwy/elixir-type_check
v0.12.0
Additions:
- The default options used are now fetched from the application configuration. This means that you can configure a default for your app as well as for each of your dependencies(!) by adding
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
andapply!
to wrap a call to any function with the function spec type that you give it.
TypeCheck.Defstruct.defstruct!
, a way to combinedefstruct
,@enforce_keys
and the creation of the struct's type, reducing boilerplate and the possibility of mistakes. (c.f. #118 )
Fixes:
v0.11.0
Wooh, this is a big release!
Most important features:
- We now support all of Elixir's builtin basic types!
- We now support all of the remote types of the Elixir standard library!
- Support for most of the map-type syntactic sugars.
- An optional Credo check to enforce that all your functions have a spec.
Full changelog
Additions
- Support for fancier map syntaxes:
%{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.- TypeCheck now supports nearly all kinds of map types that see use. Archaic combinations of optional and required are not supported, but also not very useful types in practice.
- Because of this, the inspection of the builtin type
map(key, value)
has been changed to look the same as an optional map. This is a minor backwards-incompatible change.
- Desugaring
%{}
has changed from 'any map' to 'the empty map' in line with Elixir's Typespecs. This is a minor backwards-incompatible change. - Support for the builtin types
port()
,reference()
and (based on these)identifier()
. - Support for the builtin type
struct()
. - Support for the builtin type
timeout()
. - Support for the builtin type
nonempty_charlist()
andmaybe_improper_list
and (based on these)iolist()
andiodata()
. - Adding types depending on these builtins to the default type overrides. We now support all modules of the full standard library!
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).
Fixes
- The
TypeCheck.Builtin
module is now actually spectested itself. Some consistency bugs were found and solved as a result.
v0.10.7
v0.10.4
v0.10.3
Fixes
Ensures calls to 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 )
v0.10.2
Version 0.10.0
Version 0.10.0 has been released! Ⲗ
Additions & Improvements
Support for function-types (for typechecks as well as property-testing generators):
(-> result_type)
(...-> result_type)
(param_type, param2_type -> result_type)
Type-checking function-types
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:
- When a call to
TypeCheck.conforms/3
(and variants) or a function wrapped with a@spec!
is called, we can immediately check whether a particular parameter:- is a function
- accepts the expected arity
- Then, the parameter-which-is-a-function is wrapped in a 'wrapper function' which, when called:
- typechecks whether the passed parameters are of the expected types (This checks whether your function uses the parameter-function correctly.)
- calls the original function with the parameters.
- typechecks whether the result is of the expected type. (This checks whether the parameter-function works correctly.)
- returns the result.
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!
Data generation for function types
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).
Fixes
Version 0.9.0
Version 0.9.0 has been released! 😊
Additions & Improvements
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 exactlysize
bits long),<<_ :: _ * unit>>
(matching any bitstring ofx * unit
wherex :: pos_integer()
),<<_ :: size, _ :: _ * unit>>
(matching any bitstring ofsize + x * unit
wherex :: pos_integer()
).
Property-testing generators have also been constructed for them, so you can immediatly start using them with spectests.
Version 0.8.1
Fixes
- Improved documentation with a page comparing TypeCheck with Elixir's plain typespecs. c.f. #59
- Addition of missing override for the type
Range.t()
. c.f. #58
Thank you very much, @baldwindavid 💚 !
Version 0.8.0
Version 0.8.0 has been released! 🚢
Additions & Improvements
- Pretty-printing of types and TypeError output in multiple colors:
(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.
- Nicer indentation of errors. Amongst other things, this means that error-highlighting in the documentation now works correctly (although in 100% 'red').
use TypeCheck
now also callsrequire TypeCheck.Type
so there no longer is a need to call this manually if you want to e.g. useTypeCheck.Type.build/1
(which is rather common if you want to test out particular types quickly in IEx).- named types are now printed in abbreviated fashion if they are repeated multiple times in an error message. This makes a nested error message much easier to read, especially for larger specs.
- Remote/user-defined types are now also 'named types' which profit from this change.
For instance in above example picture, we first talk aboutRating.t()
andString.t()
and only when looking at the problem in detail do we expand this to%Rating{}
andbinary()
. [type]
no longer creates afixed_list(type)
but instead alist(type)
(just as Elixir's own typespecs.)- Support for
[...]
and[type, ...]
as alias fornonempty_list()
andnonempty_list(type)
respectively.
Fixes
- Fixes prettyprinting of
TypeCheck.Builtin.Range
. - Remove support for list literals with multiple elements.
- Improved documentation. c.f. #56
Thank you very much, @baldwindavid and @0urobor0s !