Как вывести один и тот же тип в нескольких параметрах функции

Я пытаюсь основать все типы функций, связанных с API, на одном определении, чтобы упростить некоторые общие случаи использования и сделать вещи более прозрачными. Например:

// TypeScript 3.4.3
type API = 
  {route: "helloWorld", method: "get", parameters: {}, result: string}
| {route: "echo", method: "get", parameters: {arg: string}, result: {response: string}}

type GETParameters<R> = Extract<API, {route: R, method: "get"}>["parameters"];

// const a: GETParameters<"echo"> = 42; // fails, as expected
const a: GETParameters<"echo"> = {arg: "hello"}; // works, as expected

Для вышеупомянутых случаев это работает как требуется, однако я не могу заставить это работать без аннотации типа, которая уместна в следующем случае.

function f<R>(route: R, paramF: (p: GETParameters<R>) => any){
    return 0
}

// f<"echo">("helloWorld", p => p.arg)     // fails, as expected
// f<"echo">("echo", p => p.something)     // fails, as expected
f<"echo">("echo", p => p.arg)              // works, as expected, but very ugly in extreme cases

f("echo", p => p.arg)    // fails, why?
//inferred: f(route: string, paramF: (p: {} | { arg: string; }) => {}): number

Я предполагаю, что средство проверки типов выводит два экземпляра R в f<R> отдельности, но я не могу понять, как сделать вывод из обоих аргументов (предпочтительно из маршрута).

Всего 1 ответ


Typescript не будет выводить строковые литеральные типы для параметра типа, если он не расширяет string (то же самое относится и к другим литеральным типам). Если вы добавите ограничение к R оно будет работать как положено. Вы можете использовать string (и она выведет соответствующий литеральный тип), но в вашем случае я думаю, что лучше использовать точное объединение возможных маршрутов, это было бы API['route']

// TypeScript 3.4.3
type API = 
{route: "helloWorld", method: "get", parameters: {}, result: string}
| {route: "echo", method: "get", parameters: {arg: string}, result: {response: string}}

type GETParameters<R> = Extract<API, {route: R, method: "get"}>["parameters"];

//This also works:
//function f<R extends string>(route: R, paramF: (p: GETParameters<R>) => any){
function f<R extends API['route'] >(route: R, paramF: (p: GETParameters<R>) => any){
    return 0
}

f("echo", p => p.arg)

Есть идеи?

10000