Skip to content

Instantly share code, notes, and snippets.

@oplanre
Last active May 28, 2024 12:22
Show Gist options
  • Save oplanre/7ba61d6e5d02c29ed7c1bdac826928d2 to your computer and use it in GitHub Desktop.
Save oplanre/7ba61d6e5d02c29ed7c1bdac826928d2 to your computer and use it in GitHub Desktop.

Revisions

  1. oplanre revised this gist Apr 24, 2024. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions TSGen.php
    Original file line number Diff line number Diff line change
    @@ -37,7 +37,7 @@ private static function toCamelCase(string $string): string
    // Generate TypeScript interface definitions from JSON data
    private static function generateInterface(array $data, string $interfaceName): string
    {
    return "interface $interfaceName" . self::generateInterfaceImpl($data) . ";\n";
    return "interface $interfaceName " . self::generateInterfaceImpl($data) . ";\n";
    }

    private static function indent(string $code, int $level = 1): string
    @@ -50,15 +50,15 @@ private static function generateInterfaceImpl(array $data, int $indent = 0): str
    $indent++;

    foreach ($data as $key => $value) {
    array_push($definitions, self::indent("$key:" .
    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[] = "}\n";
    $definitions[] = "}";
    return implode("\n", $definitions);
    }
    private static function getTSType($value): string
    @@ -67,7 +67,7 @@ private static function getTSType($value): string
    'integer', 'double' => 'number',
    'boolean' => 'boolean',
    'string' => 'string',
    'array' => 'Array<any>',
    'array' => self::guessArrayElementType($value),
    'NULL' => 'null',
    default => 'any'
    };
    @@ -81,4 +81,4 @@ private static function guessArrayElementType(array $data): string
    }
    return "Array<" . implode(' | ', array_unique($types)) . ">";
    }
    }
    }
  2. oplanre created this gist Apr 24, 2024.
    84 changes: 84 additions & 0 deletions TSGen.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    <?php
    class TSGen
    {

    public static function fromJSON(string $json, string $interfaceName): string
    {
    try {
    return self::generateInterface(json_decode($json, true, 512, JSON_THROW_ON_ERROR), $interfaceName);
    } catch (JsonException $e) {
    throw new InvalidArgumentException("Error parsing JSON: " . $e->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[] = "}\n";
    return implode("\n", $definitions);
    }
    private static function getTSType($value): string
    {
    return match (gettype($value)) {
    'integer', 'double' => 'number',
    'boolean' => 'boolean',
    'string' => 'string',
    'array' => 'Array<any>',
    '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)) . ">";
    }
    }
    53 changes: 53 additions & 0 deletions usage.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,53 @@
    <?php
    $usage = [
    "json" => '{
    "name": "John",
    "age": 30,
    "isStudent": true,
    "address": {
    "street": "123 Main St",
    "city": "Anytown"
    },
    "emails": [
    "[email protected]",
    "[email protected]"
    ]
    }',
    "array" => [
    "name" => "John",
    "age" => 30,
    "isStudent" => true,
    "address" => [
    "street" => "123 Main St",
    "city" => "Anytown"
    ],
    "emails" => [
    "[email protected]",
    "[email protected]"
    ]
    ],
    "object" => fn(array $vars) => new readonly class ($vars) {
    public string $name;
    public int $age;
    public bool $isStudent;
    public array $address;
    public array $emails;
    public function __construct(array $vars)
    {
    foreach ($vars as $key => $value) {
    $this->$key = $value;
    }
    }
    }
    ];
    echo TSGen::fromArray($usage['array'], 'Person');
    try {
    echo TSGen::fromJSON($usage['json'], 'Person');
    } catch (InvalidArgumentException $e) {
    echo "Error: " . $e->getMessage();
    }
    try {
    echo TSGen::fromObject($usage['object']((array)$usage['array']), 'Person');
    } catch (InvalidArgumentException $e) {
    echo "Error: " . $e->getMessage();
    }