typescript - Varför ändras en snitt-typ när typerna omordnas?


Jag ber om ursäkt om detta är en mycket grundläggande fråga. Jag lär mig TypeScript på egen hand och enligt min handledning borde ordningen på typerna i en intersection inte spela någon roll, t.ex. A & B och B & A borde vara samma typ.

Men jag snubblade över detta specialfall där ordningen på typerna faktiskt spelar roll, även om jag inte förstår varför.

abstract class Test
{
    constructor(a: string, b: boolean)
    { }
}

type A = typeof Test;
type B = new (...args: unknown[]) => unknown;

type AB = ConstructorParameters<A & B>; // unknown[]
type BA = ConstructorParameters<B & A>; // [a: string, b: boolean]

Hur påverkar ordningen den resulterande typen i detta specifika fall?

Finns det några fler kantfall jag bör vara medveten om?

Tack.

Svar #1

Tagen från dokumenten:

När man drar slutsatser från en typ med flera anropssignaturer (såsom typen av en överlagrad funktion) görs slutsatserna från den sista signaturen (som förmodligen är det mest tillåtande fångst-fall). Det är inte möjligt att utföra överlagringslösning baserat på en lista med argumenttyper.

Mer förklaring kan du hitta här

När du gjorde snittet av två funktioner betyder det att du gjorde en överbelastning.

Se nästa exempel:

type A = (a: number) => number
type B = (b: string) => string
type C = (c: any) => any
declare var foo: A & B & C

foo(2) // number
foo('2') // string
foo({}) // any 

Ovanstående kod är lika med:

    function bar(a: string): string;
    function bar(a: number): number;
    function bar(a: any): any;
    function bar(a: number | string | any) {
        return null as any

    }
    bar(2); // number
    bar('2') // string
    bar({}) // any

Tänk på att jag satte mindre specifik a:any i slutet. Och det fungerar som förväntat.

Men, vad om jag sätter det i början:

    function bar(a: any): any;
    function bar(a: string): string;
    function bar(a: number): number;
    function bar(a: number | string | any) {
        return null as any

    }
    bar(2); // any
    bar('2') // any
    bar({}) // any
}

Alla ReturnTypes är any. Eftersom mindre specifik överlagring är i början.

Detsamma här:

type A = (a: number) => number
type B = (b: string) => string
type C = (c: any) => any
declare var foo: C & B & A

foo(2) // any
foo('2') // any
foo({}) // any 

UPPDATERING

Låt oss gräva i ConstructorParameters. Vad betyder det?

// this is what ConstructorParameters doing under the hood
type Check = A extends (a: infer A) => any ? A : never // number
type Check2 = (A & B) extends (a: infer A) => any ? A : never // string
type Check3 = (A & B & C) extends (a: infer A) => any ? A : never // any

Det tar bara den senast korsade posten och härleder dess parametrar.

Så om jag sätter A som sista objekt, kommer det endast att härleda A-parametrar:

    type Check4 = (C & B & A) extends (a: infer A) => any ? A : never // number

Så om du gör skärningen av funktioner - bör du behandla det som överlagring.

Kantfall: om du har funktionstyper med generics - snittet kanske inte fungerar. Jag vet inte/förstår varför.

Relaterade artiklar