Created
November 26, 2024 06:34
-
-
Save voxxal/6ce53d150919ba0f5cadeeeaca3af3c9 to your computer and use it in GitHub Desktop.
typescript utility types in zig
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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