| I have a stupid question (web dev, but also have used Java and C#). TypeScripts type system is commonly described as implementing "structural types" (not "duck" typed) And there are a ton of pragmatically sound gotchas around this, such as the importance of whether an object is used as an argument or a parameter, or if it is declared using an object literal. (focusing on objects here because for primitives I'd assume that TS has almost nominal typing) So, is there an ELI5 for the difference between structural typing and duck typing? I'd guess that duck typing is more similar to the callee perspective in TS (give me an object with the properties I need), while structural typing is more similar to the checks performed when initializing an object literal with a declared type? But I'm not sure if I'd be able to differentiate both terms succinctly. What is the relationship between duck typing and "type narrowing"? Or is that more of a "structural typing" thing? |
Likely just semantics. You could argue that the sliver of difference is just treating something like a particular API shape hence duck typing over structurally matching to an interface. For practical purposes however it does not matter. The runtime vs compile time argument is purely an implementation detail that has no relation to structural matching aka duck typing (heh) otherwise.
In practice, I find this idea rather uncomfortable and C# has been avoiding it except for few very defined cases that are least likely to cause this confusion, like GetEnumerator and Enumerator.MoveNext+Current (it could be used as an enumerable in foreach loops but unless the type implements IEnumerable, you can't coerce it to the type).
For the cases where specialization is needed, it is usually written as
No type confusion is possible as whatever is passed to the method has to declare it implements IDataArg. Same applies to 'arg' being 'IPooledArg' - it could have methods with names that would make it match the interface structurally but this is, quite literally, not a type-safe assumption. The authors may have explicitly decided not to expose it, or to provide explicit interface implementation that differs from pre-existing names.All this is very upfront and you won't be finding yourself having to rename methods or be surprised when your objects or structs are used in a way they are not supposed to because their triangle-shaped type happened to fit in a square-shaped interface hole.