Yes.
> does it infer at use time that the formatName(...) invocation is valid because the argument has a "name" property?
No. OCaml does have a structural object system (https://v1.realworldocaml.org/v1/en/html/objects.html) and it is available to reason, but it's syntactically funky (https://reasonml.github.io/docs/en/object) and as far as I can tell TFA does not use it.
> They're separate concepts.
Yes. Which is why OCaml (and thus Reason) separates it[0] from the nominative "structures".
> Maybe I want to ensure that I'm receiving a product as it makes no sense to invoke formatName(...) on other things with names like a Cat or Person.
You're really deep into this strawmanning thing.
[0] pretty completely, you can't use nominative types in a structural context, they are not compatible. In fact structural types are sets of methods, something which doesn't exist on the nominative side.
In this case it truly is a Product and if you want to reuse a field name you'll need to enclose that type in a module.
Row polymorphic -- "thing with a name" -- is possible through the object system which is accessed through a different syntax, and works like go interfaces in practice.
And this happens all the time.
They're separate concepts. Maybe I want to ensure that I'm receiving a product as it makes no sense to invoke formatName(...) on other things with names like a Cat or Person.
And the generic "thing with a name" one is possible in TypeScript too: