Hur deklarerar man att ett objekt uppfyller ett Record med funktionsvärden som accepterar valfritt antal argument i TypeScript?
Hur deklarerar jag ett objekt som låter TypeScript härleda den striktaste typen men också begränsa dess värden till att vara funktioner som returnerar en sträng för ett godtyckligt antal funktionsparametrar.
Till exempel
const stringFactory = {
a: (): string => '',
b: (input: Array<string>): string => input.join(' '), // error
} satisfies { [K: string]: (...params: Array<unknown>) => string };
Men detta resulterar i felet
Type '(input: Array<string>) => string' is not assignable to type '(...params: unknown[]) => string'. Types of parameters 'input' and 'params' are incompatible. Type 'unknown' is not assignable to type 'string[]'.ts(2322)
Jag försökte också (i) annotera objektet stringFactory med typen { [K: string]: (...params: Array<unknown>) => string }. Detta resulterar i samma fel
(ii) att använda variation av en Record eller ett gränssnitt
const stringFactory = {
a: (): string => '',
b: (input: Array<string>): string => input.join(' '), // error
} satisfies Record<string, (...params: Array<unknown>) => string }>;
(iii) Genom att ersätta unknown med never fungerar detta, men det verkar vara en mindre än idealisk lösning
const stringFactory = {
a: (): string => '',
b: (input: Array<string>): string => input.join(' '), // error
} satisfies Record<string, (...params: Array<never>) => string }>;
(iv) Jag skulle föredra att inte använda any
Svar #1
Den lämpliga supratypen för alla funktioner som returnerar string är (...params: never) => string, med den omöjliga never-typen och inte (...args: unknown[]) => string.
Det beror på att funktioner är kontravarianta i sina parametrar, se TS:s versionsanteckningar för --strictFunctionTypes för mer information och länkar om kontravarians.
Funktionen (input: Array<string>): string => input.join(' ') är inte en (...params: unknown[]) => string eftersom den senare måste kunna ta emot vilka argument som helst som anroparen vill skicka. En sådan funktion skulle till exempel kunna anropas som f(2) och då skulle din implementation försöka använda den icke-existerande join()-metoden på ett number. Hoppsan.
Å andra sidan är det ett helt acceptabelt (...params: never) => string, eftersom den typen inte kan anropas säkert alls... du kommer aldrig att hitta en argumentlista av typen never att skicka till det. Så du kommer aldrig stöta på ett sätt att anropa (...params: never) => string som skulle få (input: Array<string>): string => input.join(' ') att krascha.
Så det korrekta sättet att skriva din kod är
const stringFactory = {
a: (): string => '', // okay
b: (input: Array<string>): string => input.join(' '), // okay
} satisfies { [k: string]: (...params: never) => string };
Playground-länk till kod
