Skip to content

Instantly share code, notes, and snippets.

@voxxal
Created November 26, 2024 06:34
Show Gist options
  • Save voxxal/6ce53d150919ba0f5cadeeeaca3af3c9 to your computer and use it in GitHub Desktop.
Save voxxal/6ce53d150919ba0f5cadeeeaca3af3c9 to your computer and use it in GitHub Desktop.
typescript utility types in zig
const std = @import("std");
const StructField = std.builtin.Type.StructField;
fn Partial(comptime inner: type) type {
var typeInfo = @typeInfo(inner);
switch (typeInfo) {
.@"struct" => |*structure| {
var fields: []const StructField = &[0]StructField{};
for (structure.fields) |field| {
switch (@typeInfo(field.type)) {
.optional => {
fields = fields ++ [_]StructField{field};
},
else => {
const default_value = @as(*align(field.alignment) const field.type, @ptrCast(@alignCast(field.default_value)));
const optional = @as(?field.type, default_value.*);
fields = fields ++ [_]StructField{.{
.name = field.name,
.type = ?field.type,
.default_value = @ptrCast(&optional),
.is_comptime = field.is_comptime,
.alignment = field.alignment,
}};
},
}
}
structure.fields = fields;
return @Type(.{ .@"struct" = structure.* });
},
else => @compileError("must pass struct to Partial"),
}
}
test "partial" {
const A = struct {
a: i32 = 123,
b: ?i32 = 456,
};
const PA = Partial(A);
const isnt = PA{};
try std.testing.expectEqual(@TypeOf(isnt.a), ?i32);
try std.testing.expectEqual(@TypeOf(isnt.b), ?i32);
}
fn Required(comptime inner: type) type {
var typeInfo = @typeInfo(inner);
switch (typeInfo) {
.@"struct" => |*structure| {
var fields: []const StructField = &[0]StructField{};
for (structure.fields) |field| {
switch (@typeInfo(field.type)) {
.optional => |non_optional| {
const default_value = @as(*align(field.alignment) const field.type, @ptrCast(@alignCast(field.default_value)));
const required: ?*const anyopaque = if (default_value.* == null) null else @ptrCast(&default_value.*.?);
fields = fields ++ [_]StructField{.{
.name = field.name,
.type = non_optional.child,
.default_value = required,
.is_comptime = field.is_comptime,
.alignment = field.alignment,
}};
},
else => {
fields = fields ++ [_]StructField{field};
},
}
}
structure.fields = fields;
return @Type(.{ .@"struct" = structure.* });
},
else => @compileError("must pass struct to Required"),
}
}
test "required" {
const A = struct {
a: i32 = 123,
b: ?i32 = 456,
// c: ?i32 = null, // should error (it does)
};
const VA = Required(A);
const isnt = VA{};
try std.testing.expectEqual(@TypeOf(isnt.a), i32);
try std.testing.expectEqual(@TypeOf(isnt.b), i32);
}
fn Pick(comptime inner: type, comptime keys: anytype) type {
var typeInfo = @typeInfo(inner);
const KeysType = @TypeOf(keys);
const keys_type_info = @typeInfo(KeysType);
if (keys_type_info != .@"struct") {
@compileError("expected tuple or struct argument, found " ++ @typeName(KeysType));
}
const keys_fields = keys_type_info.@"struct".fields;
switch (typeInfo) {
.@"struct" => |*structure| {
var fields: []const StructField = &[0]StructField{};
for (structure.fields) |field| {
var found = false;
for (keys_fields) |key| {
const possible_key: []const u8 = @ptrCast(@field(keys, key.name));
if (std.mem.eql(u8, possible_key, field.name)) found = true;
}
if (found) {
fields = fields ++ [_]StructField{field};
} else {
@compileError("field not found");
}
}
structure.fields = fields;
return @Type(.{ .@"struct" = structure.* });
},
.@"union" => @compileError("todo"),
else => @compileError("must pass struct or union to Pick"),
}
}
test "pick" {
const A = struct {
a: i32 = 123,
b: i32 = 456,
};
const PA = Pick(A, .{"a"});
const inst = PA{};
try std.testing.expectEqual(inst.a, 123);
}
fn Omit(comptime inner: type, comptime keys: anytype) type {
var typeInfo = @typeInfo(inner);
const KeysType = @TypeOf(keys);
const keys_type_info = @typeInfo(KeysType);
if (keys_type_info != .@"struct") {
@compileError("expected tuple or struct argument, found " ++ @typeName(KeysType));
}
const keys_fields = keys_type_info.@"struct".fields;
switch (typeInfo) {
.@"struct" => |*structure| {
var fields: []const StructField = &[0]StructField{};
for (structure.fields) |field| {
var found = false;
for (keys_fields) |key| {
const possible_key: []const u8 = @ptrCast(@field(keys, key.name));
if (std.mem.eql(u8, possible_key, field.name)) found = true;
}
if (!found) {
fields = fields ++ [_]StructField{field};
}
}
structure.fields = fields;
return @Type(.{ .@"struct" = structure.* });
},
.@"union" => @compileError("todo"),
else => @compileError("must pass struct or union to Omit"),
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment