package builtin import ( "fmt" "reflect" "strings" "sync" ) type typeCollector struct { mu sync.Mutex types map[string]string paramTypes map[string]bool } func newTypeCollector() *typeCollector { return &typeCollector{ types: make(map[string]string), paramTypes: make(map[string]bool), } } func (tc *typeCollector) collectTypes(argsType reflect.Type, fnType reflect.Type) []string { tc.mu.Lock() defer tc.mu.Unlock() tc.types = make(map[string]string) tc.paramTypes = make(map[string]bool) var result []string tc.collectStruct(argsType, argsType.Name()) for i := 0; i < argsType.NumField(); i++ { field := argsType.Field(i) if field.Type.Kind() == reflect.Pointer || strings.Contains(field.Tag.Get("json"), ",omitempty") { tc.collectParamType(field.Type) } } if fnType.Kind() == reflect.Func && fnType.NumOut() > 0 { lastIndex := fnType.NumOut() - 1 lastType := fnType.Out(lastIndex) if lastType.Implements(reflect.TypeOf((*error)(nil)).Elem()) { if fnType.NumOut() > 1 { tc.collectType(fnType.Out(0)) } } else { tc.collectType(lastType) } } for _, t := range tc.types { result = append(result, t) } return result } func (tc *typeCollector) collectParamType(t reflect.Type) { if t.Kind() == reflect.Pointer { tc.collectParamType(t.Elem()) return } if t.Kind() == reflect.Struct && t.Name() != "" { tc.paramTypes[t.Name()+" | null"] = true } } func (tc *typeCollector) getParamTypes() map[string]bool { return tc.paramTypes } func (tc *typeCollector) collectType(t reflect.Type) { if t.Kind() == reflect.Pointer { tc.collectType(t.Elem()) return } if t.Kind() == reflect.Struct { name := t.Name() if _, exists := tc.types[name]; !exists { tc.collectStruct(t, name) } } } func (tc *typeCollector) collectStruct(t reflect.Type, name string) { if t.Kind() != reflect.Struct { return } var fields []string for i := 0; i < t.NumField(); i++ { field := t.Field(i) if field.Anonymous || !field.IsExported() { continue } fieldName := getFieldName(field) var tsType string var isOptional bool isPointer := field.Type.Kind() == reflect.Pointer if isPointer { isOptional = true tsType = goTypeToTSType(field.Type, false) } else { isOptional = strings.Contains(field.Tag.Get("json"), ",omitempty") tsType = goTypeToTSType(field.Type, false) } if isOptional { fieldName += "?" } fields = append(fields, fmt.Sprintf("%s: %s", fieldName, tsType)) tc.collectType(field.Type) } tc.types[name] = fmt.Sprintf("interface %s {%s}", name, strings.Join(fields, "; ")) } func goTypeToTSType(t reflect.Type, isPointer bool) string { if t.Kind() == reflect.Pointer { return goTypeToTSType(t.Elem(), true) } baseType := "" switch t.Kind() { case reflect.String: baseType = "string" case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: baseType = "number" case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: baseType = "number" case reflect.Float32, reflect.Float64: baseType = "number" case reflect.Bool: baseType = "boolean" case reflect.Interface: baseType = "any" case reflect.Slice: baseType = fmt.Sprintf("%s[]", goTypeToTSType(t.Elem(), false)) case reflect.Map: if t.Key().Kind() == reflect.String && t.Elem().Kind() == reflect.Interface { baseType = "Record" } else { baseType = "Record" } case reflect.Struct: name := t.Name() if name == "" { baseType = "{}" } else { baseType = name } default: baseType = "any" } if isPointer { baseType += " | null" } return baseType }