From b010f2dfbb841a45c4f4c342826ad882042274f9 Mon Sep 17 00:00:00 2001
From: Felipe Pena <felipensp@gmail.com>
Date: Mon, 17 Mar 2025 09:37:04 -0300
Subject: [PATCH 1/3] new feature

---
 vlib/v/checker/checker.v                      |  3 ++-
 vlib/v/gen/c/cgen.v                           |  9 +++++++-
 .../comptime/comptime_typeof_value_test.v     | 18 ++++++++++++++++
 vlib/v/type_resolver/comptime_resolver.v      | 21 ++++++++++++++++++-
 4 files changed, 48 insertions(+), 3 deletions(-)
 create mode 100644 vlib/v/tests/comptime/comptime_typeof_value_test.v

diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v
index e63e31c8f7bddb..e82870410f1e59 100644
--- a/vlib/v/checker/checker.v
+++ b/vlib/v/checker/checker.v
@@ -1641,7 +1641,8 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
 			else {
 				if node.field_name == 'name' {
 					return ast.string_type
-				} else if node.field_name in ['idx', 'unaliased_typ'] {
+				} else if node.field_name in ['idx', 'unaliased_typ', 'key_type', 'value_type',
+					'element_type'] {
 					return ast.int_type
 				} else if node.field_name == 'indirections' {
 					return ast.int_type
diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v
index 78c619d48aa9fd..6147ef2bb3ac03 100644
--- a/vlib/v/gen/c/cgen.v
+++ b/vlib/v/gen/c/cgen.v
@@ -3971,7 +3971,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
 					g.type_name(name_type)
 					return
 				} else if node.field_name in ['idx', 'unaliased_typ'] {
-					// `typeof(expr).idx`, // `typeof(expr).unalised_typ`
+					// `T.idx`, `T.unaliased_typ`, `typeof(expr).idx`, `typeof(expr).unalised_typ`
 					mut name_type := node.name_type
 					if node.expr is ast.TypeOf {
 						name_type = g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(node.expr.expr,
@@ -3981,6 +3981,13 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
 						g.write(int(g.unwrap_generic(name_type)).str())
 					}
 					return
+				} else if node.field_name in ['key_type', 'value_type', 'element_type'] {
+					// `T.<field_name>`, `typeof(expr).<field_name>`
+					mut name_type := node.name_type
+					name_type = g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(node.expr,
+						name_type), node.field_name)
+					g.write(int(name_type).str())
+					return
 				} else if node.field_name == 'indirections' {
 					mut name_type := node.name_type
 					if node.expr is ast.TypeOf {
diff --git a/vlib/v/tests/comptime/comptime_typeof_value_test.v b/vlib/v/tests/comptime/comptime_typeof_value_test.v
new file mode 100644
index 00000000000000..a26e880c73e86a
--- /dev/null
+++ b/vlib/v/tests/comptime/comptime_typeof_value_test.v
@@ -0,0 +1,18 @@
+fn t[T](a T) string {
+	$if a is $map {
+		mut s := ''
+		s += '${typeof[T]().name}\n'
+		s += '${T.key_type} | ${typeof(T.key_type).name} | ${typeof[T]().key_type} | ${typeof(typeof[T]().key_type).name}\n'
+		s += '${T.value_type} | ${typeof(T.value_type).name} | ${typeof[T]().value_type} | ${typeof(typeof[T]().value_type).name}\n'
+		s += '${T.idx} | ${typeof(T.idx).name} | ${typeof[T]().idx} | ${typeof(typeof[T]().idx).name}'
+		return s
+	} $else $if a is $array {
+		return '${typeof[T]().name} >> ${T.element_type} | ${typeof(T.element_type).name} | ${typeof[T]().element_type} | ${typeof(typeof[T]().element_type).name}'
+	}
+	return ''
+}
+
+fn test_main() {
+	assert t(map[u8]string{}) == 'map[u8]string\n11 | u8 | 11 | u8\n21 | string | 21 | string\n105 | int | 105 | int'
+	assert t([]rune{}) == '[]rune >> 22 | rune | 22 | rune'
+}
diff --git a/vlib/v/type_resolver/comptime_resolver.v b/vlib/v/type_resolver/comptime_resolver.v
index 9bd5a956c4c38a..53de0f5160bc9b 100644
--- a/vlib/v/type_resolver/comptime_resolver.v
+++ b/vlib/v/type_resolver/comptime_resolver.v
@@ -79,11 +79,20 @@ pub fn (mut t TypeResolver) typeof_type(node ast.Expr, default_type ast.Type) as
 		if f := t.table.find_field_with_embeds(sym, node.field_name) {
 			return f.typ
 		}
+	} else if node is ast.SelectorExpr && node.name_type != 0 {
+		if node.field_name in ['value_type', 'element_type'] {
+			return t.table.value_type(t.resolver.unwrap_generic(node.name_type))
+		} else if node.field_name == 'key_type' {
+			sym := t.table.sym(t.resolver.unwrap_generic(node.name_type))
+			if sym.info is ast.Map {
+				return t.resolver.unwrap_generic(sym.info.key_type)
+			}
+		}
 	}
 	return default_type
 }
 
-// typeof_field_type resolves the typeof[T]().<field_name> type
+// typeof_field_type resolves the T.<field_name> and typeof[T]().<field_name> type
 pub fn (mut t TypeResolver) typeof_field_type(typ ast.Type, field_name string) ast.Type {
 	match field_name {
 		'name' {
@@ -98,6 +107,16 @@ pub fn (mut t TypeResolver) typeof_field_type(typ ast.Type, field_name string) a
 		'indirections' {
 			return ast.int_type
 		}
+		'key_type' {
+			sym := t.table.final_sym(t.resolver.unwrap_generic(typ))
+			if sym.info is ast.Map {
+				return t.resolver.unwrap_generic(sym.info.key_type)
+			}
+			return ast.no_type
+		}
+		'value_type', 'element_type' {
+			return t.table.value_type(t.resolver.unwrap_generic(typ))
+		}
 		else {
 			return typ
 		}

From f0b8e185bc1a345e2325ce655090c0a05cde6922 Mon Sep 17 00:00:00 2001
From: Felipe Pena <felipensp@gmail.com>
Date: Mon, 17 Mar 2025 10:23:42 -0300
Subject: [PATCH 2/3] fix

---
 vlib/v/tests/comptime/comptime_typeof_value_test.v | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vlib/v/tests/comptime/comptime_typeof_value_test.v b/vlib/v/tests/comptime/comptime_typeof_value_test.v
index a26e880c73e86a..3f281010fa9b5e 100644
--- a/vlib/v/tests/comptime/comptime_typeof_value_test.v
+++ b/vlib/v/tests/comptime/comptime_typeof_value_test.v
@@ -13,6 +13,6 @@ fn t[T](a T) string {
 }
 
 fn test_main() {
-	assert t(map[u8]string{}) == 'map[u8]string\n11 | u8 | 11 | u8\n21 | string | 21 | string\n105 | int | 105 | int'
+	assert t(map[u8]string{}) == 'map[u8]string\n11 | u8 | 11 | u8\n21 | string | 21 | string\n111 | int | 111 | int'
 	assert t([]rune{}) == '[]rune >> 22 | rune | 22 | rune'
 }

From 67dd9f313cf0e18e0ddaaeaf63d3fef2fedb6bb5 Mon Sep 17 00:00:00 2001
From: Felipe Pena <felipensp@gmail.com>
Date: Tue, 18 Mar 2025 08:56:45 -0300
Subject: [PATCH 3/3] fix

---
 vlib/v/tests/comptime/comptime_typeof_value_test.v | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/vlib/v/tests/comptime/comptime_typeof_value_test.v b/vlib/v/tests/comptime/comptime_typeof_value_test.v
index 3f281010fa9b5e..a2b15cb5c10a66 100644
--- a/vlib/v/tests/comptime/comptime_typeof_value_test.v
+++ b/vlib/v/tests/comptime/comptime_typeof_value_test.v
@@ -2,17 +2,17 @@ fn t[T](a T) string {
 	$if a is $map {
 		mut s := ''
 		s += '${typeof[T]().name}\n'
-		s += '${T.key_type} | ${typeof(T.key_type).name} | ${typeof[T]().key_type} | ${typeof(typeof[T]().key_type).name}\n'
-		s += '${T.value_type} | ${typeof(T.value_type).name} | ${typeof[T]().value_type} | ${typeof(typeof[T]().value_type).name}\n'
-		s += '${T.idx} | ${typeof(T.idx).name} | ${typeof[T]().idx} | ${typeof(typeof[T]().idx).name}'
+		s += '${T.key_type == typeof[u8]().idx} | ${typeof(T.key_type).name} | ${typeof[T]().key_type == typeof[u8]().idx} | ${typeof(typeof[T]().key_type).name}\n'
+		s += '${T.value_type == typeof[string]().idx} | ${typeof(T.value_type).name} | ${typeof[T]().value_type == typeof[string]().idx} | ${typeof(typeof[T]().value_type).name}\n'
+		s += '${T.idx == typeof[T]().idx} | ${typeof(T.idx).name} | ${typeof(typeof[T]().idx).name}'
 		return s
 	} $else $if a is $array {
-		return '${typeof[T]().name} >> ${T.element_type} | ${typeof(T.element_type).name} | ${typeof[T]().element_type} | ${typeof(typeof[T]().element_type).name}'
+		return '${typeof[T]().name} >> ${T.element_type == typeof[rune]().idx} | ${typeof(T.element_type).name} | ${typeof[T]().element_type == typeof[rune]().idx} | ${typeof(typeof[T]().element_type).name}'
 	}
 	return ''
 }
 
 fn test_main() {
-	assert t(map[u8]string{}) == 'map[u8]string\n11 | u8 | 11 | u8\n21 | string | 21 | string\n111 | int | 111 | int'
-	assert t([]rune{}) == '[]rune >> 22 | rune | 22 | rune'
+	assert t(map[u8]string{}) == 'map[u8]string\ntrue | u8 | true | u8\ntrue | string | true | string\ntrue | int | int'
+	assert t([]rune{}) == '[]rune >> true | rune | true | rune'
 }