For example, you can have a List of type List<string>, which would be a list of any length containing any string.
On the other hand, a good tuple implementation (especially a typed one) could allow tuples like (string, integer, boolean), i.e. only arrays of length 3, where the first positions holds a string, the second position holds an integer and the third position holds a boolean. So yes, usually they are represented as arrays, but on a higher level, they are fundamentally different.
As for unions, the parent comment is not referring to the Set operation, he's referring to Union Types.
With this concept, a variable can be one of many types. For example: var input: string | number And at Runtime you would have to find out whether it is a string or a number, to be able to use it.
(Not here arguing whether Dart does or does not have these language features, just trying to clarify this misunderstanding)
For example, in C, you can define a Union type that can hold either a string or an integer, declare a variable of that type and initialize it with a string, and then simply assert that it is an integer.
I think that with your assertion of "valuable in catching bugs" you are referring to Sum Types (often called tagged unions). For Sum Types, if a language supports this concept, it usually also forces you to cover all cases at compile time, whereas with plain unions, you can shoot yourself in the foot, if you're willing to do so.