Microsoft/TypeScript

Control flow analysis for dependant parameters does not work with complex types

Open

#51,693 opened on Nov 30, 2022

View on GitHub
 (5 comments) (7 reactions) (0 assignees)TypeScript (48,455 stars) (6,726 forks)batch import
Experience EnhancementHelp WantedSuggestion

Description

Bug Report

🔎 Search Terms

dependant parameters
discriminated union parameters

🕗 Version & Regression Information

TypeScript 4.6.x -> TypeScript 4.9.3 & Nightly

  • This is the behavior in every version I tried, and I reviewed the FAQ

⏯ Playground Link

Playground link with relevant code

💻 Code

The release notes for 4.6 explain how control flow analysis for dependant parameters can work for functions whose arguments are defined as tuples:

type Func = (...args: ["a", number] | ["b", string]) => void;

const f1: Func = (kind, payload) => {
    if (kind === "a") {
        payload.toFixed();  // 'payload' narrowed to 'number'
    }
    if (kind === "b") {
        payload.toUpperCase();  // 'payload' narrowed to 'string'
    }
};

f1("a", 42);
f1("b", "hello");

However, as soon as you use a more complex type, the code no longer compiles:

type A = { kind: "a"; }
type B = { kind: "b"; }

type Func = (...args: [A, number] | [B, string]) => void;

const f1: Func = ({ kind }, payload) => {
    if (kind === "a") {
        payload.toFixed();  // 'payload' should be narrowed to 'number'
    }
    if (kind === "b") {
        payload.toUpperCase();  // 'payload' should be narrowed to 'string'
    }
};

f1({ kind: "a" }, 42);
f1({ kind: "b" }, "hello");

🙁 Actual behavior

On the line with payload.toFixed(), TypeScript gives the following compiler error:

Property 'toFixed' does not exist on type 'string | number'.
  Property 'toFixed' does not exist on type 'string'.

On the line with payload.toUpperCase(), TypeScript gives the following compiler error:

Property 'toUpperCase' does not exist on type 'string | number'.
  Property 'toUpperCase' does not exist on type 'number'.(2339)

🙂 Expected behavior

I would expect TypeScript to apply the same control flow analysis for the more complex typed parameters as the simple ones. I'm actually trying to use this feature while playing around with the new decorators that @rbuckton has been working on. I want to make a property decorator work with both accessors and setters, taking advantage of control flow analysis to return the appropriate value without casting.

Contributor guide