简体   繁体   中英

typescript: generic constraints cause type inference to pick wrong candidate?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM