typescript 2.8 Exclude: möjligt att förstärka 'partition'-typen?


Jag skulle vilja förbättra typen av partitionsfunktionen i mitt TS-bibliotek.

Om man tillämpar det på Array, är typen:

function partition<T>(list: T[], predicate:(x:T)=>boolean): [T[],T[]];

Och ett exempel på anrop skulle vara:

[1,2,3,4].partition(x => x%2===0)
=> [[2,4],[1,3]]

Nu lägger TypeScript 2.8 till villkorstyper, och i synnerhet Exclude (observera att TypeScript 2.8 vid denna tidpunkt endast är i kandidatsläppsstadiet).

Så min idé för att förbättra typen av partition, så att det är möjligt att dra nytta av typvakter, skulle vara något i den stilen:

function partition<T>(list: T[], predicate:(x:T)=>boolean): [T[],T[]];
function partition<T,U extends T>(list: T[], predicate:(v:T)=>v is U): [U[],Array<Exclude<T,U>>];

Tyvärr kan jag inte få detta att kompileras med --strict med TypeScript 2.8rc.

Här är ett exempelprogram:

function partition<T>(list: T[], predicate:(x:T)=>boolean): [T[],T[]];
function partition<T,U extends T>(list: T[], predicate:(v:T)=>v is U): [U[],Array<Exclude<T,U>>];
function partition<T,U extends T>(list: T[], predicate:(v:T)=>v is U): [U[],Array<Exclude<T,U>>] {
    return <any>[];
}

Kompilera med TS 2.8rc med --strict för att återskapa byggfelet, vilket är:

test.ts(2,10): error TS2394: Overload signature is not compatible with function implementation.

Om man tittar på definitionen av Exclude har jag också försökt ändra den andra definitionen så att den returnerar, istället för [T[],T[]], snarare [T[], Array<T|never>>], men det hjälpte inte, jag får fortfarande samma fel.

Är det möjligt att uppnå det jag vill med TypeScript 2.8?

UPPDATERING Titian Cernicova-Dragomirs svar får det att fungera, men det var på det reducerade testfallet, det fungerar fortfarande inte för mig i en klass:

class Vector<T> {
    partition<U extends T>(predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T, U>>];
    partition(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
    partition<U extends T>(predicate:(v:T)=>boolean):  [Vector<U>,Vector<Exclude<T, U>>] {
        return <any>[];
    }
}

Tydligen utlöser det extra kontroller eller något liknande att göra det i en klass, eftersom det är samma kod som fungerar utanför en klass...

Det här fungerar:

class Vector<T> {}

function partition<T,U extends T>(list: Vector<T>, predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T, U>>];
function partition<T>(list: Vector<T>, predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
function partition<T,U extends T>(list: Vector<T>, predicate:(v:T)=>boolean):  [Vector<U>,Vector<Exclude<T, U>>] {
    return <any>[];
}

Men bara att lägga koden i klassen gör att det går sönder...

Svar #1

Din implementeringssignatur behöver vara lite mer inkluderande, predikatet kan inte vara en typvakt, eftersom det i vissa överlagringar inte är en typvakt. Även ordningen på överlagringarna behöver vara annorlunda, eftersom den första kommer att fånga alla fall som den andra gör, signaturer betraktas i deklarationsordning.

function partition<T,U extends T>(list: T[], predicate:(v:T)=>v is U): [U[],Array<Exclude<T, U>>];
function partition<T>(list: T[], predicate:(x:T)=>boolean): [T[],T[]];
function partition<T,U extends T>(list: T[], predicate:(v:T)=>boolean):  [U[],Array<Exclude<T, U>>] {
    return <any>[];
}
// Usage
declare let arr: (string| number)[];

function isNumber(n: string| number) : n is number {
    return typeof n === 'number';
}

let k = partition(arr, isNumber) // will be [number[], string[]]

För Vector-exemplet kan signaturen använda Vector<any> som kommer att vara kompatibel med både Vector<T> och Vector<Exclude<T, U>>. Jag är inte säker på varför det är tillåtet i det första exemplet men inte i detta.

class Vector<T> {
    partition<U extends T>(predicate:(v:T)=>v is U): [Vector<U>,Vector<Exclude<T, U>>];
    partition(predicate:(x:T)=>boolean): [Vector<T>,Vector<T>];
    partition<U extends T>(predicate:(v:T)=>boolean):  [Vector<U>,Vector<any>] {
        return <any>[];
    }
}

declare var v: Vector<string| number>;
function isNumber(n: string| number) : n is number {
    return typeof n === 'number';
}
var d = v.partition(isNumber) // will be  [Vector<number>, Vector<string>]