microsoft / microsoft/TypeScript

type inference breaks with composition function using extend on generic arguments

Open
#44,384 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.4k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

Bug Report

Type inference breaks with composition function using extend on generic arguments. I came across this issue while trying to implement composition for type guard functions see. it worked mostly but in some cases was getting strange type error demonstrated here, then I tried to change type guards with functions and the issue has persisted. Meaning that it's more general issue and not specific to type guards.

🔎 Search Terms

type guard composition extend inference

🕗 Version & Regression Information

Tested on ts@4.0-4.4 and all the versions have this issue

⏯ Playground Link

Playground link with relevant code

💻 Code
type Func<Input, Output> = (value: Input) => Output;

const flow =<I, O extends I, O2 extends O>
  (f: Func<I, O>,g: Func<O, O2>): Func<I, O2> =>
    (i: I): O2 => g(f(i))

const pipe = <I, O>
  (i: I, f: Func<I,O>): O =>
    f(i)


type ABC = AB | "C"
type AB = "A" | "B"
declare const ab: AB | undefined
declare const isB: Func<ABC,"B">
declare function notUndefined<T>(input: T | undefined): T


const b: "B" = pipe(
    ab,
    // infered type is:
    // flow<
    //   AB | undefined
    // , AB | undefined <---- here is error `udefined` shuold be removed
    // , "B">
    // ( f: Func<AB | undefined, AB | undefined>
    // , g: Func<AB | undefined, "B">
    // ): Func<...>
    flow(notUndefined, isB)
    //                 ~~~
    // because of incorectly infered type getting this error:
    //  Argument of type 'Func<ABC, "B">' is not assignable to parameter of type 'Func<AB | undefined, "B">'.
    //   Type 'AB | undefined' is not assignable to type 'ABC'.
    //     Type 'undefined' is not assignable to type 'ABC'.(2345)
    
    // if I specify type arguments it will work fine:
    // flow<AB | undefined,AB,"B">(notUndefined, isB)   
)
 


// NOTE: if we were to use notUndefined without generic argument all would be just fine:
// declare function notUndefined(input: AB | undefined): AB

// NOTE: both of this work just fine
const abnn1: AB = pipe(ab, notUndefined)
const abnn2: AB = pipe(ab, flow(notUndefined,notUndefined))

// NOTE following lines compiling proves that `isB: Func<ABC,"B">` is asignable to `isB2: Func<AB, "B">` 
declare const isB2: Func<AB, "B">
declare const acceptIsB2: (check: Func<AB, "B">) =>  void
acceptIsB2(isB)
acceptIsB2(isB2)
🙁 Actual behavior

as shown in code snippet type is inferred incorrectly and I'm getting compiler error .

🙂 Expected behavior

type should be inferred correctly.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the linked TypeScript Playground repro and comparing flow(notUndefined, isB) with the explicitly supplied type arguments. Trace how generic arguments are inferred for the composition function. Done means the sample infers the intermediate type without including undefined and compiles with the expected "B" result.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.