package tsconvert import ( "fmt" "reflect" "strings" ) // ConvertType converts a Go reflect.Type to a TypeScript type string. func ConvertType(t reflect.Type) string { return goTypeToTSType(t, false) } // goTypeToTSType converts a Go type to TypeScript type string. // isPointer tracks if we're inside a pointer chain. func goTypeToTSType(t reflect.Type, isPointer bool) string { // Handle Pointer Types if t.Kind() == reflect.Pointer { return goTypeToTSType(t.Elem(), true) } // Determine Base Type 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" } // Add Null for Pointer Types if isPointer { baseType += " | null" } return baseType } // CollectTypes extracts all type declarations from a function signature. // It analyzes the args type and return type to find all struct types. func CollectTypes(argsType, fnType reflect.Type) *TypeSet { // Create TypeSet ts := NewTypeSet() // Collect Types from Args Struct collectStructTypes(argsType, ts) // Collect Types from Return 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 { collectType(fnType.Out(0), ts) } } else { collectType(lastType, ts) } } return ts } // collectType recursively collects struct types. func collectType(t reflect.Type, ts *TypeSet) { // Handle Pointer Types if t.Kind() == reflect.Pointer { collectType(t.Elem(), ts) return } // Collect Struct Types if t.Kind() == reflect.Struct { name := t.Name() if name != "" { // Only Process if Not Already Processed if _, exists := ts.Get(name); !exists { collectStructTypes(t, ts) } } } } // collectStructTypes converts a Go struct to TypeScript interface and adds to TypeSet. func collectStructTypes(t reflect.Type, ts *TypeSet) { // Validate Struct Type if t.Kind() != reflect.Struct { return } // Get Struct Name name := t.Name() if name == "" { return // Skip Anonymous Structs } // Check if Already Processed if _, exists := ts.Get(name); exists { return } // Collect Fields var fields []string for i := 0; i < t.NumField(); i++ { field := t.Field(i) // Skip Anonymous and Unexported Fields if field.Anonymous || !field.IsExported() { continue } // Get Field Name fieldName := getFieldName(field) // Determine Type and Optionality var tsType string var isOptional bool isPointer := field.Type.Kind() == reflect.Pointer if isPointer { isOptional = true tsType = goTypeToTSType(field.Type, true) } else { isOptional = strings.Contains(field.Tag.Get("json"), ",omitempty") tsType = goTypeToTSType(field.Type, false) } // Mark Optional Fields if isOptional { fieldName += "?" } fields = append(fields, fmt.Sprintf(" %s: %s", fieldName, tsType)) // Recursively Collect Nested Types collectType(field.Type, ts) } // Add Type Definition definition := fmt.Sprintf("interface %s {\n%s\n}", name, strings.Join(fields, "\n")) _ = ts.Add(name, definition) } // getFieldName extracts the field name from json tag or uses the Go field name. func getFieldName(field reflect.StructField) string { // Get JSON Tag jsonTag := field.Tag.Get("json") if jsonTag != "" && jsonTag != "-" { name, _, _ := strings.Cut(jsonTag, ",") return name } // Use Go Field Name return field.Name } // GenerateFunctionDecl creates a TypeScript function declaration. func GenerateFunctionDecl(name string, argsType, fnType reflect.Type, isAsync bool) string { // Validate Args Type if argsType.Kind() != reflect.Struct { return "" } // Collect Parameters var params []string for i := 0; i < argsType.NumField(); i++ { field := argsType.Field(i) fieldName := getFieldName(field) goType := field.Type // Determine Type and Optionality var tsType string var isOptional bool isPointer := goType.Kind() == reflect.Pointer if isPointer { isOptional = true tsType = goTypeToTSType(goType, true) if !strings.Contains(tsType, " | null") { tsType += " | null" } } else { isOptional = strings.Contains(field.Tag.Get("json"), ",omitempty") tsType = goTypeToTSType(goType, false) } // Mark Optional Fields if isOptional { fieldName += "?" } params = append(params, fmt.Sprintf("%s: %s", fieldName, tsType)) } // Determine Return Type returnSignature := "any" 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 { returnType := fnType.Out(0) returnSignature = goTypeToTSType(returnType, returnType.Kind() == reflect.Pointer) } } else { returnSignature = goTypeToTSType(lastType, lastType.Kind() == reflect.Pointer) } } // Wrap in Promise for Async Functions if isAsync { returnSignature = fmt.Sprintf("Promise<%s>", returnSignature) } // Generate Declaration return fmt.Sprintf("declare function %s(%s): %s;", name, strings.Join(params, ", "), returnSignature) }