от
Я боролся с именованием этого вопроса - открыт для его изменения. Я довольно новичок в машинописи и пытаюсь использовать API универсальным, безопасным и расширяемым способом. Вдохновленный RESTyped, я определил общий интерфейс «определения API»:
interface ApiBase {
    [route: string]: ApiRoute   
}

interface ApiRoute {
    query: { [key: string]: string }
    body: any
    response: any
}

interface ApiSpec {
    [route: string]: {
        [method: string]: ApiRoute  
    }
}
и это можно использовать для определения типов для нескольких конечных точек API, например:
interface MyApi extends ApiSpec {
    "/login": {
        "POST": {
            body: {
                username: string,
                password: string
            },
            response: {
                token: string
            }   
        }   
    },
    "/user": {
        "GET": {
            query: {
                "username": string
            },
            response: {
                "email": string,
                "name": string
            }
        }
    }
}
Я подозреваю, что универсальный класс может использовать эти типы и предоставлять безопасные для них методы. Что-то вроде:
const api = ApiService();
api.post("/login", {
    // This body is typesafe 

Где метод post()
не будет компилироваться, если объект не соответствует
body
, определенному в интерфейсе
MyApi
. К сожалению, я довольно заблудился из-за того, куда идти отсюда. Что-то вроде этого:
class ApiService {
    post(route: string, body: T[route].body): T[route].response {
        // todo
    }
}
Который, очевидно, не компилируется. Как я могу получить доступ к подтипу в интерфейсе
MyApi
?
T[route].body
определенно не так. Как мне это сделать? ура РЕДАКТИРОВАТЬ ------------------------------------------ Я немного почитал, и, думаю, я куда-то доберусь! Это работает на машинописной площадке:
class ApiService {
    async post>(
        route: Path,
        data: API[Path]["POST"]["body"]
    ): Promise {
        const resp = await fetch(route, {
            method: "POST",
            body: JSON.stringify(data),
        });
        return await resp.json();
    }
}
И отлично работает при вызове существующего маршрута:
const api = new ApiService();

// Will give an error if the wrong "body" is passed in!
api.post("/login", {
    username: "johnny99",
    password: "rte"
});
но это также работает при вызове маршрута, который не существует, а это не то, чего я хочу.
// Should error, but doesn't!
api.post("/bad", {
    whatever: ""
});
Я также немного обеспокоен моей реализацией
post()
- что происходит, когда объект, заданный
resp.json()
, отличается от того, что определено в определении типа? Выдает ли это ошибку времени выполнения - я должен всегда вызывать ее в
try/catch
охранники, или я могу каким-то образом заставить Promise провалиться вместо этого?              

Ваш ответ

Отображаемое имя (по желанию):
Конфиденциальность: Ваш электронный адрес будет использоваться только для отправки уведомлений.
Анти-спам проверка:
Чтобы избежать проверки в будущем, пожалуйста войдите или зарегистрируйтесь.

1 Ответ

0 голосов
от
Прежде чем я получил ответ, я попытался воспроизвести вашу ситуацию на игровой площадке и заметил, что мне нужно изменить тип
ApiRoute
на
interface ApiRoute {
  query?: { [key: string]: string }; // optional
  body?: any; // optional
  response: any;
}
чтобы избежать ошибок. Если это не было для вас ошибкой, это потому, что вы не используете
, что вам действительно следует. Я предполагаю, что теперь мы проводим строгую проверку на ноль.



Я думаю, что ваша проблема здесь в том, что ваш интерфейс ApiSpec
говорит, что у него есть свойства
ApiRoute
для каждого возможного ключа и каждого возможного подключа:
declare const myApi: MyApi;
myApi.mumbo.jumbo; // ApiRoute
myApi.bad.POST.body; // any
Этот код не является ошибкой. Итак, когда вы звоните
api.post("/bad", {
  whatever: ""
});
по сути, вы просто ищете свойство
body
какого-то
myApi.bad.POST
, что не является ошибкой. Так как же это исправить? Я думаю, что было бы более разумно выразить тип
ApiSpec
в качестве общего ограничения на возможные типы, подобные
MyApi
, а не на конкретный тип с парой вложенных индексных сигнатур:
type EnsureAPIMeetsSpec
...