Add column type guessing from map return function
As discussed [here](https://github.com/huggingface/datasets/issues/5965), there are some cases where datasets is unable to automatically promote columns during mapping. The fix is to explicitly provide a `features` definition so pyarrow can configure itself with the right column types from the outset.

This PR provides an alternative approach, which is functionally equivalent to specifying features but a bit cleaner within a larger mapping pipeline. It allows clients to typehint the return variable coming from the mapper function - if we find one of these type annotations specified, and no explicit features have been passed in, we'll try to convert it into a Features map. If the map function runs and casting is unable to succeed, it will raise a DatasetTransformationNotAllowedError that indicates the typehint may be to blame. It works for batched and non-batched mapping functions.

Currently supported column types:
- builtins primitives: string, int, float, bool
- dictionaries, lists (nested and one-deep)
- Optional types and None-Unions (synonymous with optional types)

It's used like:

```python
class DatasetTyped(TypedDict):
  texts: list[str]

def dataset_typed_map(batch) -> DatasetTyped:
  return {"texts": [text.split() for text in batch["raw_text"]]}

dataset = {"raw_text": ["", "This is a test", "This is another test"]}

with Dataset.from_dict(dataset) as dset:
  new_dataset = dset.map(
    dataset_typed_map,
    batched=True,
    batch_size=1,
    num_proc=1,
)
```

Open questions:
- Should logging indicate we have automatically guessed these types? Or proceed quietly until we hit an error (as is the current implementation).