Repository: TypeScript
Issue: [Feature Request] Non-Union Generic Type Params
Labels: Suggestion,Awaiting More Feedback

<!-- 🚨 STOP 🚨 𝗦𝗧𝗢𝗣 🚨 𝑺𝑻𝑶𝑷 🚨

Half of all issues filed here are duplicates, answered in the FAQ, or not appropriate for the bug tracker. Please read the FAQ first, especially the "Common Feature Requests" section.

-->

## Search Terms

+ non-union
+ generic
+ type param
+ one of

## Suggestion

A way to annotate that a generic type param **will not** accept union types.
I don't have the syntax or keyword in mind, but I'll just go ahead and use `nonUnion` as a kind of type param modifier,
```ts
type NonUnionX<nonUnion T> = (
  /*Implementation*/
);
```

## Use Cases

#### What do you want to use this for?

I work with pretty complex types (in my opinion); and **a lot** of them.

When working on a complex type, I tend to break the problem down into smaller subproblems.

The base case is usually assuming that all type params are **non-union**.
After that, I build on top of the base case and implement types that support union type params.

-----

Here is an example type that is a base case,
[`PrimaryKey_NonUnion<TableT>`](https://github.com/AnyhowStep/tsql/blob/master/src/primary-key/primary-key.ts#L19)

-----

And here is an example of a type building upon the base case,
[`PrimaryKey_Output<TableT>`](https://github.com/AnyhowStep/tsql/blob/master/src/primary-key/primary-key.ts#L31)

It distributes `TableT` and uses `PrimaryKey_NonUnion`. The result is a union if `TableT` is a union.

-----

And here is an example of another type building upon the base case,
[`PrimaryKey_Input<TableT>`](https://github.com/AnyhowStep/tsql/blob/master/src/primary-key/primary-key.ts#L43)

It distributes `TableT` and uses `PrimaryKey_NonUnion`.
Then, it uses `UnionToIntersection<>` to combine the results into one type.

-----

That experimental repository of mine is filled with instances of generic types that support union types and those that do not.

There are times where you **really** do not want to pass a union type to a generic type param because it'll result in bugs that may not be noticed till later.

#### What shortcomings exist with current approaches?

One approach is to just write a comment that says,
```ts
/**
 * + Assumes `T` is not a union
 * + Assumes `U` may be a union
 * + Assumes `V` is not a union
 */
```

This gets **very** error-prone when you start having hundreds of types.

-----

Another approach is to give your types names that are descriptive,
```ts
type SomeOperation_NonUnionT_UnionU_NonUnionV<
  T,
  U,
  V
> = (
  //Implementation
);
```

This is still error-prone; you may still use it incorrectly.
Even if the name of the type says `NonUnionT`, you may still pass a union type to `T`.

## Examples

```ts
type NonUnionX<nonUnion T> = (
  /*Implementation*/
);
//OK
type nonUnionX1 = NonUnionX<string>;
//Error, Type `NonUnionX` expects non-union for type parameter `0`
//  `string|number` is a union type
type nonUnionX2 = NonUnionX<string|number>;
//                          ~~~~~~~~~~~~~

type UnionX<T> = (
  T extends any ?
  //OK! `T` has been distributed
  NonUnionX<T> : 
  never
);
//OK
type unionX1 = UnionX<string>;
//OK
type unionX2 = UnionX<string|number>;

type Blah<T> = (
  //Error, Type `NonUnionX` expects non-union for type parameter `0`
  //  `T` may be a union type
  NonUnionX<T>
  //        ~
);
```

-----

A **non-approach** is to expose only types that handle union type params and to leave non-union implementations unexported.

This is not a useful approach because it means building new types in a different file using these non-union implementations becomes impossible. Since they're unexported.

## Checklist

My suggestion meets these guidelines:

* [X] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [X] This wouldn't change the runtime behavior of existing JavaScript code
* [X] This could be implemented without emitting different JS based on the types of the expressions
* [X] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
* [X] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).

## Similar issues

https://github.com/microsoft/TypeScript/issues/24085
https://github.com/microsoft/TypeScript/issues/27808