ข้ามไปยังเนื้อหา

Reflection

ใน TypeScript คุณตรวจ type metadata ที่ runtime ได้ผ่าน reflect-metadata และ decorator — แต่ต้อง opt-in เอง: เปิด experimentalDecorators กับ emitDecoratorMetadata ใน tsconfig.json และ metadata ก็ต้อง attach ด้วยมือจาก decorator function เอง JavaScript ลบข้อมูล type ออกเกือบหมดตอน runtime decorator จึงเป็นแค่ workaround ไม่ใช่ language primitive

Go เป็นภาษา compile เหมือนกัน แต่ต่างจาก TypeScript ตรงที่เก็บข้อมูล type ไว้ครบที่ runtime ผ่าน reflect package ทุกค่าใน Go รู้ type ของตัวเองตอน runtime — ไม่ต้องมี annotation ไม่ต้องตั้ง build flag

TypeScript
// TypeScript — ต้องใช้ reflect-metadata + decorator compiler flags
import 'reflect-metadata';
function Column(target: any, key: string) {
const type = Reflect.getMetadata('design:type', target, key);
console.log(`${key}: ${type.name}`);
}
class User {
@Column name: string = '';
@Column age: number = 0;
}
Go
// Go — built-in ไม่ต้องใช้ flags
import "reflect"
type User struct {
Name string
Age int
}
u := User{Name: "Alice", Age: 30}
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Printf("%s: %s\n", f.Name, f.Type)
}
// Name: string
// Age: int

Struct tag คือ string literal ที่ฝังอยู่ในการประกาศ field มองไม่เห็นตอน compile time และอ่านได้เฉพาะตอน runtime ผ่าน reflect นี่คือวิธีที่ encoding/json, scanner ของ database/sql และ validation library อย่าง go-playground/validator รู้ว่า JSON key ไหน map กับ field ไหน

TypeScript
// TypeScript — class-validator ใช้ decorators
import { IsEmail, IsString, Min } from 'class-validator';
class CreateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
@Min(0)
age: number;
}
Go
// Go — struct tags (plain strings อ่านผ่าน reflect)
type CreateUserDTO struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"min=0"`
}
// encoding/json ใช้ "json" tag อัตโนมัติ
// Validator libraries อ่าน "validate" ที่ runtime

pattern มาตรฐานสำหรับอ่าน tag คือ reflect.Type.Field(i).Tag.Get("key"):

func printJSONTags(v any) {
t := reflect.TypeOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem() // dereference pointer
}
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
jsonKey := f.Tag.Get("json")
fmt.Printf("Field: %-10s json=%q\n", f.Name, jsonKey)
}
}

reflect.TypeOf ให้ type descriptor (kind, field, method) ส่วน reflect.ValueOf ให้ boxed value ที่คุณตรวจดูได้ และ — ถ้าระวังพอ — แก้ค่าได้ด้วย

u := User{Name: "Alice", Age: 30}
// Type introspection
t := reflect.TypeOf(u)
fmt.Println(t.Kind()) // struct
fmt.Println(t.NumField()) // 2
// Value introspection
v := reflect.ValueOf(u)
fmt.Println(v.Field(0)) // Alice
fmt.Println(v.Field(1)) // 30
// Mutation ต้องใช้ pointer และ CanSet()
vp := reflect.ValueOf(&u).Elem()
vp.Field(0).SetString("Bob")
fmt.Println(u.Name) // Bob
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age"`
}
func printStructTags(v any) {
t := reflect.TypeOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
fmt.Println("--- Struct tags ---")
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Printf("%-8s json=%q validate=%q\n",
f.Name,
f.Tag.Get("json"),
f.Tag.Get("validate"),
)
}
}
func printValues(v any) {
rv := reflect.ValueOf(v)
rt := rv.Type()
fmt.Println("--- Values ---")
for i := 0; i < rv.NumField(); i++ {
fmt.Printf("%-8s = %v\n", rt.Field(i).Name, rv.Field(i))
}
}
func main() {
u := User{Name: "Alice", Email: "[email protected]", Age: 30}
printStructTags(u)
printValues(u)
}
ฟังก์ชันใดที่ return runtime type descriptor ของค่า Go?
ต้องทำอะไรก่อนเรียก SetString() บน reflect.Value field?
Go struct tags เก็บและอ่านจากที่ไหน?
เมื่อใดควรเลือก generics แทน reflect สำหรับ utility function ใหม่?