Automatic type conversion not being applied

Why can’t automatic type conversion happen here?

typedef PluginSpec = {
    final name:String;
    final ?cmd:String;
    final ?event:String;

abstract Plugin(PluginSpec) {
    public static inline function from(spec:PluginSpec):Plugin {
        return untyped __lua__("{{0}, cmd = {1}, event = {2}, config = {3}}",, spec.cmd, spec.event, spec.config);

typedef Plugins = lua.Table.Table<Int, Plugin>;

extern class Packer {
    inline static function init(plugins:Plugins):Void { ... }

When I try to use it like this:

import lua.Table.create as t;

function main() {
    Packer.init(t([{name: "wbthomason/packer.nvim", cmd: "", event: ""}]));

or this

import lua.Table.create as t;

function main() {
    final plugins:Plugins = t([{name: "wbthomason/packer.nvim", cmd: "", event: ""}]);

It says the types do not align.

src/kickstart/Pure.hx:7: characters 2-84 : error: { name : String, event : String, cmd : String } should be plugins.Plugin
src/kickstart/Pure.hx:7: characters 2-84 : ... have: lua.Table<..., { name, event, cmd }>
src/kickstart/Pure.hx:7: characters 2-84 : ... want: plugins.Plugins<>

Might be because cmd and event are optional in the PluginSpec type, meaning their types are Null?

Haxe can’t infer too many type conversions at once. Sometimes you have to explicitly tell it to convert a type. (Last I checked, this is intentional, to avoid edge cases where it might make the wrong inference.)

Here, you can either tell it that the array is an Array<Plugin>, or you can tell it that the first item in the array is a Plugin, because it always assumes the remaining items match the first.

You can either do this using a variable declaration, or with type check syntax. I strongly discourage using a cast, because that could suppress actual errors.

Indeed the problem was (well, it still is) the depth of “type hops”. I solved it by removing one nest level:

// This does not work
  typedef Plugins = lua.Table.Table<Int, Plugin>;
  inline static function init(plugins:Plugins):Void { ... } 
// This works, no extra type declaration, inline it
  inline static function init(plugins:Array< Plugin >):Bool {