@Type
@Type builds a type from compile-time type information.
That sounds abstract, so start with the simple version:
const MyInt = u32;Here, MyInt is just another name for the type u32.
But @Type lets you create a type from a structured description:
const T = @Type(.{
.int = .{
.signedness = .unsigned,
.bits = 32,
},
});This creates the type u32.
You will not use @Type often as a beginner. It is an advanced builtin for generic code, reflection, and metaprogramming. But it is useful to understand what it does, because it completes the pair with @typeInfo.
@typeInfo breaks a type apart.
@Type builds a type from parts.
The Pair: @typeInfo and @Type
Think of the relationship like this:
const info = @typeInfo(u32);
const T = @Type(info);The first line asks:
What is the structure of u32?The second line asks:
Build a type from this structure.So for many types, this is the rough idea:
@Type(@typeInfo(T)) == TThis means: inspect a type, then rebuild it.
You usually do not write this exact code in normal programs, but it explains the concept.
Creating an Integer Type
Here is a small example:
const std = @import("std");
const U32 = @Type(.{
.int = .{
.signedness = .unsigned,
.bits = 32,
},
});
pub fn main() void {
const x: U32 = 123;
std.debug.print("{}\n", .{x});
}This program creates a 32-bit unsigned integer type.
It is equivalent to using u32.
const U32 = u32;The @Type version is longer because it describes the type manually.
Signed and Unsigned Integers
You can build signed integer types too:
const I64 = @Type(.{
.int = .{
.signedness = .signed,
.bits = 64,
},
});This creates i64.
The two important fields are:
.signedness
.bitsThe signedness says whether the integer can represent negative numbers.
The bits field says how many bits the integer uses.
Why This Exists
At first, @Type may look unnecessary. Why write this:
const U32 = @Type(.{
.int = .{
.signedness = .unsigned,
.bits = 32,
},
});when you can write this?
const U32 = u32;For ordinary code, you should write the simple version.
@Type becomes useful when the type is computed.
Example: you want to create an unsigned integer type with a bit width chosen at compile time.
fn UInt(comptime bits: u16) type {
return @Type(.{
.int = .{
.signedness = .unsigned,
.bits = bits,
},
});
}Now you can write:
const U7 = UInt(7);
const U13 = UInt(13);
const U128 = UInt(128);Zig supports arbitrary-width integer types, so u7 and u13 are valid integer types.
A Working Example
const std = @import("std");
fn UInt(comptime bits: u16) type {
return @Type(.{
.int = .{
.signedness = .unsigned,
.bits = bits,
},
});
}
pub fn main() void {
const U7 = UInt(7);
const x: U7 = 100;
std.debug.print("{}\n", .{x});
}A 7-bit unsigned integer can store values from 0 to 127, so 100 fits.
This would not fit:
const x: U7 = 200;The compiler rejects it because 200 is too large for 7 bits.
@Type Returns a Type
This point matters:
const U7 = UInt(7);U7 is a type, not a value.
Then you use it as a type:
const x: U7 = 100;This is the same pattern as:
const MyNumber = u32;
const x: MyNumber = 100;In Zig, types can be passed around and returned at compile time.
That is one of the main reasons comptime is powerful.
Creating Array Types
@Type can also build array types.
For example:
const Bytes16 = @Type(.{
.array = .{
.len = 16,
.child = u8,
.sentinel = null,
},
});This creates:
[16]u8You can use it like this:
const std = @import("std");
const Bytes16 = @Type(.{
.array = .{
.len = 16,
.child = u8,
.sentinel = null,
},
});
pub fn main() void {
const data: Bytes16 = [_]u8{0} ** 16;
std.debug.print("len = {}\n", .{data.len});
}Output:
len = 16Again, in normal code you should usually write [16]u8. But when the length or child type is computed at compile time, @Type can be useful.
A Generic Array Type Builder
fn Array(comptime Child: type, comptime len: usize) type {
return @Type(.{
.array = .{
.len = len,
.child = Child,
.sentinel = null,
},
});
}Use it like this:
const Buffer = Array(u8, 1024);This creates:
[1024]u8A complete example:
const std = @import("std");
fn Array(comptime Child: type, comptime len: usize) type {
return @Type(.{
.array = .{
.len = len,
.child = Child,
.sentinel = null,
},
});
}
pub fn main() void {
const Buffer = Array(u8, 8);
const buf: Buffer = [_]u8{0} ** 8;
std.debug.print("{}\n", .{buf.len});
}Creating Pointer Types
@Type can describe pointer types too.
Pointer type descriptions are more detailed because pointers have several properties:
const PtrToU8 = @Type(.{
.pointer = .{
.size = .one,
.is_const = false,
.is_volatile = false,
.alignment = @alignOf(u8),
.address_space = .generic,
.child = u8,
.is_allowzero = false,
.sentinel = null,
},
});This creates a type like:
*u8Do not worry if this feels dense. Pointer types carry a lot of information in Zig. They are not just “addresses.” They also encode constness, alignment, address space, child type, and pointer kind.
For beginner code, write *u8 directly.
Use @Type only when you need to construct such a type programmatically at compile time.
@Type Is a Metaprogramming Tool
Metaprogramming means writing code that works with code-like structures.
In Zig, types are compile-time values. @Type allows you to construct those values.
This is useful for:
generic containers
serialization libraries
binary format readers
compile-time validation
reflection systems
automatic wrappers
low-level APIs that need exact type construction
Most application code does not need @Type.
But library code sometimes uses it heavily.
Do Not Overuse It
A beginner should not reach for @Type first.
Prefer normal type syntax:
u32
[]const u8
[16]u8
*const Foo
struct { x: i32, y: i32 }Use @Type only when the type has to be built from compile-time information.
Clear code is better than clever type construction.
This is clear:
const Buffer = [1024]u8;This is more complex:
const Buffer = @Type(.{
.array = .{
.len = 1024,
.child = u8,
.sentinel = null,
},
});They describe the same type, but the first version is easier to read.
Key Idea
@Type creates a type from compile-time type information.
It is the inverse of @typeInfo.
Use @typeInfo when you want to inspect a type.
Use @Type when you want to build a type.
For beginners, the main lesson is simple: Zig treats types as compile-time values, and @Type is the builtin that can construct those values explicitly.