Skip to content
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

Bad type inference under --strictFunctionTypes #19576

Closed
gcnew opened this issue Oct 30, 2017 · 6 comments
Closed

Bad type inference under --strictFunctionTypes #19576

gcnew opened this issue Oct 30, 2017 · 6 comments
Assignees
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@gcnew
Copy link
Contributor

gcnew commented Oct 30, 2017

TypeScript Version: 2.7.0-dev.20171029

Code

type Comparator<T> = (x: T, y: T) => number;

interface LinkedList<T> {
    comparator: Comparator<T>,
    nodes: Node<T>
}

type Node<T> = { value: T, next: Node<T> } | null

declare function compareNumbers(x: number, y: number): number;
declare function mkList<T>(items: T[], comparator: Comparator<T>): LinkedList<T>;

const list: LinkedList<number> = mkList([], compareNumbers); // `T` inferred as `never`,
                                                             //     should   be `number`

Actual behavior:

The implicit evolving never[] type of the array literal is taking precedence, leading to unexpected results.

@HerringtonDarkholme
Copy link
Contributor

I think this is an intended behavior. compareNumbers type argument is covariant. This means number must be a supertype of T. Empty array is typed initially as never, a subtype of every other type. So, type checker should choose never, so number is correctly typed covariantly.

I would suggest annotate mkList<number>

@kitsonk
Copy link
Contributor

kitsonk commented Oct 31, 2017

That does feel like never is infecting the inference though. While it might be strictly sound it feels like resolution should only assign never when there is no other suitable type.

@gcnew
Copy link
Contributor Author

gcnew commented Oct 31, 2017

I think the main problem is that an unresolved contextually typed literal takes precedence. The array is not a variable reference, it's a literal. What surfaces is the presumably evolving implicit never[] type, which is unexpected because it trumps user provided annotations.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Oct 31, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Nov 1, 2017

One option here is to make no inferences form a fresh array literal. the parallel in an object literal case here would be:

declare function mkList<T>(items: { 0?: T }, comparator: Comparator<T>): LinkedList<T>;
mkList({}, compareNumbers); // `number`, since no inferences are made from `{}`

@gcnew
Copy link
Contributor Author

gcnew commented Nov 1, 2017

Yes, I think that would be the best approach, but only if the array is empty.

@RyanCavanaugh
Copy link
Member

Example of breaking soundness with an aliased never[]

type Comparator<T> = (x: T, y: T) => number;

declare const compareStrings: Comparator<string>;
declare const compareNumber: Comparator<number>;

function makeArray<T1, T2>(arr1: T1[], arr2: T2[], c1: Comparator<T1>, c2: Comparator<T2>) {
    return {
        arr1,
        arr2
    }
}

const arr: never[] = [];
const res = makeArray(arr, arr, compareStrings, compareNumber);

res.arr1.push("");
res.arr2.push(10);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

6 participants