Vyhledávání na webu

Jak deklarovat, že objekt splňuje Record s funkcemi, které přijímají libovolný počet argumentů v TypeScriptu?


Jak deklaruji objekt, který umožňuje TypeScriptu odvodit nejpřísnější typ, ale zároveň omezuje jeho hodnoty na funkci, která vrací řetězec pro libovolný počet parametrů funkce.

Například

const stringFactory = {
  a: (): string => '',
  b: (input: Array<string>): string => input.join(' '), // error
} satisfies { [K: string]: (...params: Array<unknown>) => string };

Nicméně to vede k chybě

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) 

Také jsem se pokusil (i) anotovat objekt stringFactory typem { [K: string]: (...params: Array<unknown>) => string }. To vede ke stejné chybě

(ii) použití varianty záznamu nebo rozhraní

const stringFactory = {
  a: (): string => '',
  b: (input: Array<string>): string => input.join(' '), // error
} satisfies Record<string, (...params: Array<unknown>) => string }>;

(iii) Nahrazením unknown výrazem never to funguje, ale zdá se to být méně než ideální řešení

const stringFactory = {
  a: (): string => '',
  b: (input: Array<string>): string => input.join(' '), // error
} satisfies Record<string, (...params: Array<never>) => string }>;

(iv) Raději bych nepoužíval any

Odpověď č. 1

Správný supertyp pro všechny funkce, které vracejí string, je (...params: never) => string, používající nemožný typ never, nikoli (...args: unknown[]) => string.

To proto, že funkce jsou kontrvariantní ve svých parametrech, podívejte se na poznámky k vydání TS pro --strictFunctionTypes pro více informací a odkazů o kontravariantnosti.

Funkce (input: Array<string>): string => input.join(' ') není (...params: unknown[]) => string, protože ta druhá musí přijmout jakékoliv argumenty, které chce volající předat. Taková funkce by mohla být volána například jako f(2) a vaše implementace by pak zkusila použít neexistující metodu join() na number. Ups.

Na druhou stranu je to zcela přijatelný (...params: never) => string, protože tento typ nelze bezpečně vůbec volat... nikdy nenajdete seznam argumentů typu never, který byste mu mohli předat. Takže se nikdy nesetkáte se způsobem volání (...params: never) => string, který by způsobil, že (input: Array<string>): string => input.join(' ') selže.

Takže správný způsob, jak psát svůj kód, je

const stringFactory = {
  a: (): string => '', // okay
  b: (input: Array<string>): string => input.join(' '), // okay
} satisfies { [k: string]: (...params: never) => string };

Odkaz na kód na playgroundu