Skip to main content

W0083 - Type definitions should live in modules, not header files

Warning

%% my_types.hrl
-type user_id() :: binary().
%% ^^^^^^^ 💡 warning: Type definitions should live in modules, not header files.

Explanation

This diagnostic fires on type definitions — -type, -opaque and -nominal — declared in a header file (.hrl).

A header is textually included, so a type defined there is redefined in every module that includes it. There is no single module that owns the type, nothing to -export_type it from, and no way for a caller to refer to it as module:type(). -opaque in a header is worse still: every including module gets its own copy, so the opacity it promises is not enforced anywhere.

Fix

Define the type in a module and export it:

%% my_types.erl
-module(my_types).
-export_type([user_id/0]).

-type user_id() :: binary().

Callers then refer to it as my_types:user_id(), with one definition and one owner.

Note that records are not affected by this diagnostic — -record declarations in headers remain the normal way to share a record between modules.