0

TypeScript Version: 2.6.0-dev.20170826 and 2.4.2

I wonder if I hit some typescript inference bug/limitation, or if my code is simply incorrect. If the code is actually valid, and it's a type inference issue, I'll report a bug to the typescript github.

I'm trying to constrain the Set builder to accept only types for which equality is properly defined (to avoid this problem).

strange.d.ts

declare module 'strange' {
    export type WithEquality = string|number|boolean|{equals(other: any): boolean; hashCode(): number;};

    export interface Set<T> {
        add(other: T): Set<T>;
    }

    export function makeSetUnsafe<V>(...vals: V[]): Set<V>;
    export function makeSet<V extends WithEquality>(...vals: V[]): Set<V>;
}

strange.ts

///<reference path="./strange.d.ts"/>

import * as S from 'strange';

const x = S.makeSetUnsafe(1,2,3);
x.add(4);

const y = S.makeSet(1,2,3);
y.add(4);

Expected behavior: From my understanding the code should compile. Typescript should infer number for both examples as the type, and since number is an option in WithEquality, and the constraint is T extends WithEquality, all should be fine.

Actual behavior: The call to makeSetUnsafe compiles fine, but the call to makeSet fails with this error:

strange.ts(9,7): error TS2345: Argument of type '4' is not assignable to parameter of type '1 | 2 | 3'.

Adding the generic constraint to check that the generic type of the Set interface extends WithEquality causes the inference to pick 1|2|3 instead of number for T.

Giving the type explicitly with const y = S.makeSet<number>(1,2,3); makes it build, so it seems adding the generic constraint makes the type inference pick another type.

In fact I can reproduce the issue even with the simpler

export type WithEquality = number;

A good answer would explain why this code is wrong, hopefully give a typescript implementation allowing to express these constraints with the type inference working, or confirm that it's a typescript limitation.

1 Answer 1

2

As for why that happens, check the answer to this issue:
Type variable constrained to primitive or union type not assignable to it.

You can fix it like this:

export function makeSet<V>(...vals: Array<V & WithEquality>): Set<V>;

And then:

const y = makeSet2(1,2,3);
y.add(4); // fine
const z = makeSet(/a*/); // error
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.