getMessage()); } } public static function fromArray(array $data, string $interfaceName): string { return self::generateInterface($data, $interfaceName); } //note that this only works for public properties public static function fromObject(object $object, string $interfaceName = "", bool $useNamespacedClassName = false, $glue = "_"): string { if ($interfaceName !== "") return self::generateInterface(get_object_vars($object), $interfaceName); if ((new ReflectionClass($object))->isAnonymous()) { throw new InvalidArgumentException("Anonymous classes must have an interface name"); } if (!$useNamespacedClassName) { $name = self::toCamelCase(strrchr(get_class($object), "\\")); } else { $name = self::toCamelCase(str_replace("\\", $glue, get_class($object))); } return self::generateInterface(get_object_vars($object), $name); } // Implementation details private static function toCamelCase(string $string): string { return lcfirst(str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $string)))); } // Generate TypeScript interface definitions from JSON data private static function generateInterface(array $data, string $interfaceName): string { return "interface $interfaceName " . self::generateInterfaceImpl($data) . ";\n"; } private static function indent(string $code, int $level = 1): string { return implode("\n", array_map(fn($line) => str_repeat(" ", $level) . $line, explode("\n", $code))); } private static function generateInterfaceImpl(array $data, int $indent = 0): string { $definitions = ["{"]; $indent++; foreach ($data as $key => $value) { array_push($definitions, self::indent("$key: " . match (gettype($value)) { 'array' => array_is_list($value) ? self::guessArrayElementType($value) : self::generateInterfaceImpl($value), 'object' => self::generateInterfaceImpl(get_object_vars($value)), default => self::getTSType($value) } . ";", $indent)); } $definitions[] = "}"; return implode("\n", $definitions); } private static function getTSType($value): string { return match (gettype($value)) { 'integer', 'double' => 'number', 'boolean' => 'boolean', 'string' => 'string', 'array' => self::guessArrayElementType($value), 'NULL' => 'null', default => 'any' }; } private static function guessArrayElementType(array $data): string { $types = []; foreach ($data as $value) { $types[] = self::getTSType($value); } return "Array<" . implode(' | ', array_unique($types)) . ">"; } }