Typescript: Avoid distribution of conditional types over union types

Created on 11 Jan 2019  ·  2Comments  ·  Source: microsoft/TypeScript

Search Terms

Suggestion

I would like to disable the distribution of union types over conditional types in certain cases. If this is already possible (not sure about that) it should be documented in a better way.

Use Cases

Suppose you would like to infer type parameters like so:

````ts
type Route = {
Path: string;
Params: { [key: string]: string }
}

type RouteWithParams = {
Path: string;
Params: { [key in T]: string }
}

type RouteWithParamsMaker = (keyof T['Params']) extends infer U
? U extends string
? RouteWithParams
: never
: never;

function toRouteWithParams(r: T): RouteWithParamsMaker {
return (r as unknown) as RouteWithParamsMaker;
}

const route = {
Path: 'somewhere',
Params: { id: 0, name: 1 },
}

const routeWithParams = toRoute(route);
````

The resulting variable is currently inferred to have the type

ts RouteWithParams<"id"> | RouteWithParams<"name">

Instead, I would like to receive the following type

ts RouteWithParams<"id" | "name">

I have no idea whether the current behaviour is intentional (and we would need a new annotation for such cases) or a bug.

Checklist

My suggestion meets these guidelines (not sure about the first and the last one):

  • [ ] 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.)
  • [ ] This feature would agree with the rest of TypeScript's Design Goals.
Question

Most helpful comment

To avoid distribution: wrap any naked type parameters in a 1-tuple, and do the same for the check type.

type RouteWithParamsMaker<T extends Route> = (keyof T['Params']) extends infer U
    ? [U] extends [string]
        ? RouteWithParams<U>
        : never
    : never;

I don't believe this is in the TypeScript handbook, but it probably should be.

Tag for searching purpose: distributive conditional type trouble

All 2 comments

So.... I just found out that I actually can circumvent the distribution if I change RouteWithParamsMaker:

ts type RouteWithParamsMaker <T extends Route> = (keyof T['Params']) extends infer U ? RouteWithParams<Extract<U, string>> : never;

This came a bit as a surprise - my initial feeling was that it should behave equally. So I guess one way to avoid the distribution is to extract conditionals in a clever way to new types. I would really like to have better guidance as a programmer here.

To avoid distribution: wrap any naked type parameters in a 1-tuple, and do the same for the check type.

type RouteWithParamsMaker<T extends Route> = (keyof T['Params']) extends infer U
    ? [U] extends [string]
        ? RouteWithParams<U>
        : never
    : never;

I don't believe this is in the TypeScript handbook, but it probably should be.

Tag for searching purpose: distributive conditional type trouble

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

seanzer picture seanzer  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

dlaberge picture dlaberge  ·  3Comments

fwanicka picture fwanicka  ·  3Comments