Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tvald opened this issue Feb 9, 2018 · 2 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@tvald
Copy link

tvald commented Feb 9, 2018

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>'.
@tvald
Copy link
Author

tvald commented Feb 9, 2018

@ahejlsberg @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>;
}

@lostfields
Copy link

lostfields commented Feb 9, 2018

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

@mhegazy mhegazy added the Bug A bug in TypeScript label Feb 9, 2018
@mhegazy mhegazy added this to the TypeScript 2.8 milestone Feb 9, 2018
@ahejlsberg ahejlsberg added the Fixed A PR has been merged for this issue label Feb 9, 2018
@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

4 participants