typescript - Varför behandlas namngivna funktioner annorlunda än pilfunktioner i cirkulära beroenden?


Jag försöker skapa min egen mekanism för beroendeinjektion i TypeScript, och jag har stött på ett intressant problem. När jag deklarerar mina beroenden måste jag använda namngivna funktioner istället för pilfunktioner (som jag vanligtvis använder som standard), annars får jag fel på grund av cirkulära deklarationer.

Nedan är uppsättningen av typer som utgör min beroendelösare:

type CollectionTemplate = {
    [key: string]: (...args: any) => any
}

type RegistryTemplate = {
    [key: string]: CollectionTemplate
}

type RegistryCollection<R extends RegistryTemplate> = keyof R

type CollectionEntry<C extends CollectionTemplate> = keyof C

type BaseRegistry<R extends RegistryTemplate> = R

type BaseContext<R extends RegistryTemplate> = {
    [C in RegistryCollection<R>]: {
        [E in CollectionEntry<R[C]>]: ReturnType<R[C][E]>
    }
}

type BaseUseDep<R extends RegistryTemplate> =
    <C extends RegistryCollection<R>, E extends CollectionEntry<R[C]>>
        (collection: C, entry: E) => BaseContext<R>[C][E]

Nu fungerar allt detta fint när jag använder namngivna funktioner:

type DepRegistry = BaseRegistry<typeof registry>
type DepContext = BaseContext<DepRegistry>
type UseDep = BaseUseDep<DepRegistry>

function greet(deps: UseDep)
{
    return (name: string) =>
    {
        const console = deps('system', 'console')
        console.log(name)
    }
}

function printGreeting(deps: UseDep)
{
    return () =>
    {
        const greet = deps('social', 'greet')
        greet('John')
    }
}

const registry = {
    system: {
        console: () => console,
    },
    social: {
        greet,
        printGreeting,
    }
}

Men om jag deklarerar dessa funktioner med pilfunktioner istället för namngivna funktioner får jag följande fel:

Type alias 'DepRegistry' circularly references itself. ts(2456)
Type alias 'UseDep' circularly references itself. ts(2456)

Varför är det så? Är det helt enkelt för att jag tilldelar en funktion till en variabel istället för att deklarera en uttrycklig funktion?

REDIGERA: Här är lekplatser med namngivna funktioner och med pilfunktioner.

Svar #1

följande svar förklarar delvis problemet; det var menat som en kommentar

Jag tror att anledningen bakom skillnaden mellan namngivna och pilfunktioner kan vara den lata vs ivriga lösningen av typdefinitioner.

Anders Hejlsberg jämförde interface vs type-upplösning i den här GitHub-kommentaren:

Tricket är att göra de rekursiva bakåtreferenserna inom gränssnittstyper. Detta fungerar eftersom upplösning av gränssnittsbaserade typer och gränssnittsmedlemmar skjuts upp, medan upplösning av typalias utförs omedelbart.

En signatur för en funktion kan specificeras med interface { (...params: Params): Return } och därför används en lat lösningsstrategi av Typescript-kompilatorn och rekursiva definitioner är tillåtna.

Jag vet inte varför pilfunktioner behandlas på ett annat sätt, men det verkar som att deras typsignatur utvärderas ivrigt.


Jag följer ofta ett liknande mönster av beroenden registrering/injektion och har stött på problemet tidigare. Tänkte aldrig på att ersätta pilar med vanliga funktioner dock. Bra att veta att det är en möjlighet. Du kan lika gärna överväga att undvika cirkulära beroenden från första början.

Själva problemet är mycket kontraintuitivt och mycket intressant samtidigt.

Relaterade artiklar