# `@typeInfo`

### `@typeInfo`

`@typeInfo` asks the compiler for structured information about a type.

Example:

```zig
const info = @typeInfo(u32);
```

This means:

```text
Tell me what kind of type u32 is.
```

The answer is not a string. It is a compile-time value that describes the type.

For `u32`, the information says: this is an integer type, it is unsigned, and it has 32 bits.

#### `@typeInfo` Takes a Type

This is the normal form:

```zig
const info = @typeInfo(T);
```

`T` must be a type.

For example:

```zig
const a = @typeInfo(u8);
const b = @typeInfo(i64);
const c = @typeInfo([]const u8);
```

If you have a value and want to inspect its type, combine `@TypeOf` and `@typeInfo`:

```zig
const x = 123;
const info = @typeInfo(@TypeOf(x));
```

Read this as:

```text
Get the type of x.
Then get information about that type.
```

#### A Simple Example

```zig
const std = @import("std");

pub fn main() void {
    const info = @typeInfo(u32);

    switch (info) {
        .int => |int_info| {
            std.debug.print("integer bits: {}\n", .{int_info.bits});
            std.debug.print("signedness: {}\n", .{int_info.signedness});
        },
        else => {
            std.debug.print("not an integer\n", .{});
        },
    }
}
```

Possible output:

```text
integer bits: 32
signedness: builtin.Signedness.unsigned
```

The `switch` checks what kind of type `u32` is.

Since `u32` is an integer type, the `.int` branch runs.

#### Type Information Is Tagged

`@typeInfo` returns a tagged value.

That means the result has a category, such as:

```text
int
float
bool
pointer
array
struct
enum
union
optional
error_union
function
void
```

Each category carries different information.

An integer has a bit count and signedness.

A pointer has a child type, alignment, constness, and pointer size.

An array has a child type and length.

A struct has fields and declarations.

So `@typeInfo` does not return the same shape for every type. You must switch on the type category first.

#### Inspecting an Integer

```zig
const std = @import("std");

fn describeInt(comptime T: type) void {
    const info = @typeInfo(T);

    switch (info) {
        .int => |int_info| {
            std.debug.print("bits: {}\n", .{int_info.bits});
            std.debug.print("signed: {}\n", .{int_info.signedness == .signed});
        },
        else => {
            std.debug.print("not an integer type\n", .{});
        },
    }
}

pub fn main() void {
    describeInt(u8);
    describeInt(i32);
}
```

Possible output:

```text
bits: 8
signed: false
bits: 32
signed: true
```

The function takes a type at compile time:

```zig
fn describeInt(comptime T: type) void
```

That is necessary because type information exists at compile time.

#### Inspecting an Array

Arrays have a length and a child type.

```zig
const std = @import("std");

fn describeArray(comptime T: type) void {
    const info = @typeInfo(T);

    switch (info) {
        .array => |array_info| {
            std.debug.print("array length: {}\n", .{array_info.len});
            std.debug.print("element size: {}\n", .{@sizeOf(array_info.child)});
        },
        else => {
            std.debug.print("not an array\n", .{});
        },
    }
}

pub fn main() void {
    describeArray([5]u16);
}
```

Possible output:

```text
array length: 5
element size: 2
```

For `[5]u16`, the array length is `5`, and the child type is `u16`.

#### Inspecting a Pointer

Pointers carry more information.

```zig
const std = @import("std");

fn describePointer(comptime T: type) void {
    const info = @typeInfo(T);

    switch (info) {
        .pointer => |ptr_info| {
            std.debug.print("is const: {}\n", .{ptr_info.is_const});
            std.debug.print("child size: {}\n", .{@sizeOf(ptr_info.child)});
        },
        else => {
            std.debug.print("not a pointer\n", .{});
        },
    }
}

pub fn main() void {
    describePointer(*u32);
    describePointer(*const u32);
}
```

Possible output:

```text
is const: false
child size: 4
is const: true
child size: 4
```

`*u32` and `*const u32` both point to a `u32`, but the second one points to data that should not be modified through that pointer.

`@typeInfo` can see that difference.

#### Inspecting a Struct

Structs contain fields.

```zig
const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

fn describeStruct(comptime T: type) void {
    const info = @typeInfo(T);

    switch (info) {
        .@"struct" => |struct_info| {
            inline for (struct_info.fields) |field| {
                std.debug.print("field: {s}, size: {}\n", .{
                    field.name,
                    @sizeOf(field.type),
                });
            }
        },
        else => {
            std.debug.print("not a struct\n", .{});
        },
    }
}

pub fn main() void {
    describeStruct(Point);
}
```

Possible output:

```text
field: x, size: 4
field: y, size: 4
```

Notice the syntax:

```zig
.@"struct"
```

`struct` is a keyword, so this form is used when matching the type information tag.

The `inline for` matters because `struct_info.fields` is compile-time information. The compiler unrolls the loop while compiling the program.

#### Why `@typeInfo` Is Useful

Most beginner programs do not need reflection.

But `@typeInfo` becomes useful when writing generic code.

For example, you may want a function that only accepts integer types:

```zig
fn requireInteger(comptime T: type) void {
    switch (@typeInfo(T)) {
        .int => {},
        else => @compileError("expected an integer type"),
    }
}
```

Then:

```zig
requireInteger(u32); // ok
requireInteger([]const u8); // compile error
```

This lets you enforce rules at compile time.

#### A Generic Example

Here is a function that returns the bit count of an integer type:

```zig
fn bitCount(comptime T: type) comptime_int {
    return switch (@typeInfo(T)) {
        .int => |int_info| int_info.bits,
        else => @compileError("bitCount expects an integer type"),
    };
}
```

Use it like this:

```zig
const std = @import("std");

fn bitCount(comptime T: type) comptime_int {
    return switch (@typeInfo(T)) {
        .int => |int_info| int_info.bits,
        else => @compileError("bitCount expects an integer type"),
    };
}

pub fn main() void {
    std.debug.print("u8 has {} bits\n", .{bitCount(u8)});
    std.debug.print("u64 has {} bits\n", .{bitCount(u64)});
}
```

Output:

```text
u8 has 8 bits
u64 has 64 bits
```

This function does not run normal runtime inspection. It runs using compile-time type information.

#### `@typeInfo` and `@Type`

The two builtins work together.

`@typeInfo` takes a type and returns a description.

`@Type` takes a description and builds a type.

So conceptually:

```zig
const info = @typeInfo(u32);
const T = @Type(info);
```

`T` becomes `u32`.

In normal beginner code, you rarely need this pair. But it shows a deep Zig idea: types can be inspected, passed around, and constructed at compile time.

#### Do Not Use It When Simple Code Is Enough

This is simple:

```zig
fn add(a: u32, b: u32) u32 {
    return a + b;
}
```

Do not replace it with reflection-heavy code unless you have a reason.

`@typeInfo` is powerful, but it can make code harder to read.

Use it when your code truly needs to react to the shape of a type.

Good uses include:

```text
generic containers
serialization
formatting
binary encoding
compile-time validation
testing helpers
type-safe wrappers
```

Poor uses include making ordinary code clever for no reason.

#### Key Idea

`@typeInfo(T)` gives compile-time information about type `T`.

It lets Zig code inspect integers, pointers, arrays, structs, enums, unions, optionals, functions, and other types.

For beginners, remember this simple rule:

Use `@typeInfo` when generic code needs to ask, “What kind of type is this?”

