Skip to content

identical conditional types with a callable constituent not assignable to each other #21823

Closed

Description

@tvald

This is a slightly more complicated version of #21756, and is still present after #21782. When a conditional type has a callable constituent, the types are not assignable to each other.

TypeScript Version: 2.8.0-dev.20180208 (latest master)

Code

type F1<T> = T extends 0 ? 0 : () => 0;
type F2<T> = T extends 0 ? 0 : () => 0;
const f = <T>(a: F1<T>): F2<T> => a; // fails

type G1<T> = T extends 0 ? 0 : string;
type G2<T> = T extends 0 ? 0 : string;
const g = <T>(a: G1<T>): G2<T> => a; // passes

Expected behavior:
no errors

Actual behavior:

TypeScript $ node lib/tsc --noEmit test.ts
test.ts(11,35): error TS2322: Type 'F1<T>' is not assignable to type 'F2<T>'.
  Type '0 | (() => 0)' is not assignable to type 'F2<T>'.
    Type '0' is not assignable to type 'F2<T>'.

Activity

  1. tvald commented on Feb 9, 2018

    @tvald
    Author

    Anders Hejlsberg (@ahejlsberg) Mohamed Hegazy (@mhegazy)

    This is still motivated in my particular case by the example from #21756:

    // lib/es5.d.ts
    declare type PromiseConstructorLike = new <T>(executor: (
      resolve: T extends void ? (value?: PromiseLike<void>) => void : (value: T | PromiseLike<T>) => void,
      reject: (reason?: any) => void) => void
    ) => PromiseLike<T>;
    
    // lib/es2015.promise.d.ts
    interface PromiseConstructor {
      new <T>(executor: (
        resolve: T extends void ? (value?: PromiseLike<void>) => void : (value: T | PromiseLike<T>) => void,
        reject: (reason?: any) => void) => void
      ): Promise<T>;
    }
  2. lostfields commented on Feb 9, 2018

    @lostfields

    Yeah, I think I have a similar issue in #21734 where generic constraints does not work well for anything but primary types.

    This work as it should and throws an error because of the generic constraint

    let first = <T extends Number>(items: Array<T>): T => {
        return items.shift();
    }
    
    let a = first([1,2,3]);
    let b = first(["a","b","c"]); // error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'Number[]'

    This also works as it should, it inherits TType from the generic class

    class Item<TType> {
        public first<T extends Array<TType>>(items: T) {
            return items.slice(2,1).shift();
        }
    }
    
    let c = new Item<number>().first([1,2,3]);
    let d = new Item<number>().first(["a", "b", "c"]); //error TS2345: Argument of type 'string[]' is not assignable to parameter of type 'number[]'

    But, whenever I use more complex structures as constraint it never fails

    class Car {
    }
    
    let e = new Item<Car>().third([new Car(), new Car(), new Car()]);
    let f = new Item<Car>().third([1,2,3]); // no error because signature of third is third<number[]> instead of third<Car[]>

    The issue here is that the signature for e line is Item<Car>.third<Car[]>and for f line is Item<Car>.third<number[]>, the type TType is never assigned down to method first

  3. locked and limited conversation to collaborators on Jul 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions