Core type introspection and classification for Ash types.
This module provides a centralized set of functions for determining the nature and characteristics of Ash types, including embedded resources, typed structs, unions, and primitive types.
Used throughout the codebase for type checking, code generation, and runtime processing.
Summary
Functions
Builds a reverse mapping from client names to internal names.
Classifies an Ash type into a category for processing purposes.
Gets the type and constraints for a field from field specs.
Extracts the inner type from an array type.
Gets the typescript_field_names as a map, or empty map if not available.
Extracts union types from an attribute's constraints.
Extracts union types from type and constraints directly.
Checks if constraints include non-empty field definitions.
Checks if a module has a typescript_field_names/0 callback.
Checks if a type is an Ash type module.
Checks if a type is a custom Ash type with a typescript_type_name callback.
Checks if a module is an embedded Ash resource.
Checks if a type is a primitive Ash type (not a complex or composite type).
Checks if constraints specify an instance_of that is an Ash resource.
Recursively unwraps Ash.Type.NewType to get the underlying type and constraints.
Functions
Builds a reverse mapping from client names to internal names.
Can take either a map of field names or a module with typescript_field_names/0.
Examples
iex> AshTypescript.TypeSystem.Introspection.build_reverse_field_names_map(%{is_active?: "isActive"})
%{"isActive" => :is_active?}
iex> AshTypescript.TypeSystem.Introspection.build_reverse_field_names_map(MyApp.TaskStats)
%{"isActive" => :is_active?, "meta1" => :meta_1}
Classifies an Ash type into a category for processing purposes.
Returns one of:
:union_attribute- Union type:embedded_resource- Single embedded resource:embedded_resource_array- Array of embedded resources:tuple- Tuple type:attribute- Simple attribute (default)
Parameters
type_module- The Ash type module (e.g., Ash.Type.String, Ash.Type.Union)attribute- The attribute struct containing type and constraintsis_array- Whether this is inside an array type
Examples
iex> attr = %{type: MyApp.Address, constraints: []}
iex> AshTypescript.TypeSystem.Introspection.classify_ash_type(MyApp.Address, attr, false)
:embedded_resource
Gets the type and constraints for a field from field specs.
Examples
iex> specs = [name: [type: :string], age: [type: :integer]]
iex> AshTypescript.TypeSystem.Introspection.get_field_spec_type(specs, :name)
{:string, []}
iex> AshTypescript.TypeSystem.Introspection.get_field_spec_type(specs, :unknown)
{nil, []}
Extracts the inner type from an array type.
Examples
iex> AshTypescript.TypeSystem.Introspection.get_inner_type({:array, Ash.Type.String})
Ash.Type.String
iex> AshTypescript.TypeSystem.Introspection.get_inner_type(Ash.Type.String)
Ash.Type.String
Gets the typescript_field_names as a map, or empty map if not available.
Examples
iex> AshTypescript.TypeSystem.Introspection.get_typescript_field_names_map(MyApp.TaskStats)
%{is_active?: "isActive", meta_1: "meta1"}
Extracts union types from an attribute's constraints.
Handles both direct union types and array union types.
Examples
iex> attr = %{type: Ash.Type.Union, constraints: [types: [note: [...], url: [...]]]}
iex> AshTypescript.TypeSystem.Introspection.get_union_types(attr)
[note: [...], url: [...]]
Extracts union types from type and constraints directly.
Useful when you have constraints but not the full attribute struct. Handles both direct union types and array union types.
Examples
iex> constraints = [types: [note: [...], url: [...]]]
iex> AshTypescript.TypeSystem.Introspection.get_union_types_from_constraints(Ash.Type.Union, constraints)
[note: [...], url: [...]]
Checks if constraints include non-empty field definitions.
Examples
iex> AshTypescript.TypeSystem.Introspection.has_field_constraints?([fields: [name: [type: :string]]])
true
iex> AshTypescript.TypeSystem.Introspection.has_field_constraints?([fields: []])
false
Checks if a module has a typescript_field_names/0 callback.
Examples
iex> AshTypescript.TypeSystem.Introspection.has_typescript_field_names?(MyApp.TaskStats)
true
iex> AshTypescript.TypeSystem.Introspection.has_typescript_field_names?(Ash.Type.String)
false
Checks if a type is an Ash type module.
Examples
iex> AshTypescript.TypeSystem.Introspection.is_ash_type?(Ash.Type.String)
true
iex> AshTypescript.TypeSystem.Introspection.is_ash_type?(MyApp.CustomType)
true
iex> AshTypescript.TypeSystem.Introspection.is_ash_type?(:string)
false
Checks if a type is a custom Ash type with a typescript_type_name callback.
Custom types are Ash types that define a typescript_type_name/0 callback
to specify their TypeScript representation.
Examples
iex> AshTypescript.TypeSystem.Introspection.is_custom_type?(MyApp.MyCustomType)
true
iex> AshTypescript.TypeSystem.Introspection.is_custom_type?(Ash.Type.String)
false
Checks if a module is an embedded Ash resource.
Examples
iex> AshTypescript.TypeSystem.Introspection.is_embedded_resource?(MyApp.Accounts.Address)
true
iex> AshTypescript.TypeSystem.Introspection.is_embedded_resource?(MyApp.Accounts.User)
false
Checks if a type is a primitive Ash type (not a complex or composite type).
Primitive types include basic types like String, Integer, Boolean, Date, UUID, etc.
Examples
iex> AshTypescript.TypeSystem.Introspection.is_primitive_type?(Ash.Type.String)
true
iex> AshTypescript.TypeSystem.Introspection.is_primitive_type?(Ash.Type.Union)
false
Checks if constraints specify an instance_of that is an Ash resource.
Examples
iex> AshTypescript.TypeSystem.Introspection.is_resource_instance_of?([instance_of: MyApp.Todo])
true
iex> AshTypescript.TypeSystem.Introspection.is_resource_instance_of?([])
false
Recursively unwraps Ash.Type.NewType to get the underlying type and constraints.
When a type is wrapped in one or more NewType wrappers, this function
recursively unwraps them until it reaches the base type. If the NewType
has a typescript_field_names/0 callback and the constraints don't already
have an instance_of key, it will add the NewType module as instance_of
to preserve the reference for field name mapping.
Parameters
type- The type to unwrap (e.g., MyApp.CustomType)constraints- The constraints for the type
Returns
A tuple {unwrapped_type, unwrapped_constraints} where:
unwrapped_typeis the final underlying type after all NewType unwrappingunwrapped_constraintsare the final constraints, potentially augmented withinstance_of
Examples
iex> # Simple NewType with typescript_field_names
iex> unwrap_new_type(MyApp.TaskStats, [])
{Ash.Type.Struct, [fields: [...], instance_of: MyApp.TaskStats]}
iex> # Nested NewTypes (outermost with callback wins)
iex> unwrap_new_type(MyApp.Wrapper, [])
{Ash.Type.String, [max_length: 100, instance_of: MyApp.Wrapper]}
iex> # Non-NewType (returns unchanged)
iex> unwrap_new_type(Ash.Type.String, [max_length: 50])
{Ash.Type.String, [max_length: 50]}