Skip to content

Narrow conditional typeΒ #59294

Closed as not planned
Closed as not planned

Description

πŸ” Search Terms

Conditional type inference narrow

βœ… Viability Checklist

⭐ Suggestion

A conditional type should be narrowed when possible.

βœ… When calling foo in the example below, types are as expected - We always require the x property to be defined in the object payload, and when we specify mode Y, we also require the y property to be defined

Within the foo function In the example below, the point type is completely useless
πŸ”΄ I expect the point type to be narrowed to Pick<TPoint, 'x' | 'y'> = { x: number; y: number } when we know that mode: 'Y', but I get a type error when I try to access any property of the object:
Property 'y' does not exist on type 'Pick<TPoint, TMode extends "Y" ? "x" | "y" : "x">'.(2339)
πŸ”΄ I expect x to always be accessible from the point, but I get a type error when I try to access the property of the object:
Property 'x' does not exist on type 'Pick<TPoint, TMode extends "Y" ? "x" | "y" : "x">'.(2339)

πŸ“ƒ Motivating Example

We would now be able to write functions where two or more parameters depend on each other:

type TPoint = { x: number; y: number; z: number };

const foo = <TMode extends 'X' | 'Y'>(
  mode: TMode,
  point: Pick<TPoint, TMode extends 'Y' ? 'x' | 'y' : 'x'>,
) => {
  if (mode === 'Y') {
    return point.x + point.y;
  }

  return point.x;
};


foo('X', { x: 1 });

// @ts-expect-error: Object literal may only specify known properties, and 'y' does not exist in type 'Pick<TPoint, "x">'.(2353)
foo('X', { x: 1, y: 2 });

// @ts-expect-error: Property 'y' is missing in type '{ x: number; }' but required in type 'Pick<TPoint, "x" | "y">'.(2345)
foo('Y', { x: 1 });

foo('Y', { x: 1, y: 2 });

πŸ’» Use Cases

  1. What do you want to use this for?
    When I have a function where the type of two parameters depend on each other.

  2. What shortcomings exist with current approaches?
    Using a combination of generics and conditional types, we are able to give the function a very nice type signature from the outside, but within the body of the function, the types are practically unusable.

  3. What workarounds are you using in the meantime?
    // @ts-ignore everywhere
    Alternatively wrap the parameters of the function in a single object payload

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions