#typescript #typescript-generics
#typescript #typescript-дженерики
Вопрос:
Я пытаюсь определить тип / интерфейс (не важно, какой для целей этого упражнения), который использует дженерики, обладающие двумя свойствами:
interface Response<T> {
status: number;
data: T | undefined;
}
Ограничение, которое я хочу зафиксировать, заключается в том, что при статусе !== 200 данные должны быть неопределенными. Когда статус === 200, данные должны быть T. Таким образом, мне не всегда нужно проверять, есть ли ответ.данные не определены после проверки, чтобы убедиться, что response.status равен 200:
if (response.status === 200 amp;amp; response.data) {
// Wouldn't it be nice it TS just knew that response.data is
// not undefined without having to check it explicitly?
}
Пока у меня есть это:
interface IOkResponse<T> {
status: 200;
data: T;
}
interface IErrorResponse {
status: number;
data: undefined;
}
type Response<T> = IOkResponse<T> | IErrorResponse;
Конечно, поскольку IErrorResponse не ограничивает статус числами, которые не равны 200, это не работает. Как бы мне добавить это ограничение?
Комментарии:
1. Не идеально, но вы могли бы перечислить все другие поддерживаемые вами статусы. Их всего около ~ 60, так что это не конец света.
2. Если нет лучшего способа, это то, что я намерен делать 🙂
3.TypeScript не поддерживает отрицаемые типы (над ним некоторое время работали и в конце концов отказались), поэтому нет никакого способа сказать «
number amp; not 200
«. Если бы ваш тип был строковым"200"
, можно было бы программно сгенерировать все трехзначные строковые литералы от"100"
до"599"
и исключить"200"
в TS4.1, но если вам нужен числовой литерал, вероятно, было бы лучше просто перечислить допустимые коды в ручном объединении. Если вас интересует версия строкового литерала, дайте мне знать.4. @jcalz Кстати, как я могу с тобой связаться?. Я написал тебе электронное письмо, но не уверен, что ты его прочитал
5. @KMehta смотрите эту ссылку на игровую площадку для получения дополнительной информации
Ответ №1:
Я согласен с @jcalz, нет никакого хакерского способа сделать это. Одна вещь, которую вы можете сделать, — это typeguards. Это обеспечит безопасность вашего типа кода, но вы должны заплатить за это — накладные расходы на функции.
interface IOkResponse<T> {
status: 200;
data: T;
}
interface IErrorResponse {
status: number;
data: undefined
}
type Result<T> = IOkResponse<T> | IErrorResponse;
const isOK = <T,>(arg: Result<T>): arg is IOkResponse<T> => arg.status === 200
const foo = <T,>(arg: Result<T>) => {
if (isOK(arg)) {
const y = arg // IOkResponse<T>
} else {
const z = arg // IErrorResponse;
}
}
Ответ №2:
class Response<T> {
status: number = null;
get data(): T {
return this.status === 200 amp;amp; this.data || undefined;
}
}
Как насчет того, чтобы попробовать что-то подобное?, данные будут неопределенными, если статус === 200
Ответ №3:
Хотя у @captain-yossarin есть функциональное решение, которое носит более общий характер (именно поэтому я официально принял это как ответ), вот решение, с которым я пошел…
export enum StatusCode {
OK = 200,
NoContent = 204,
NotModified = 304,
BadRequest = 400,
Unauthorized = 401,
// Other status codes that the client cares about go here
}
interface IOkResponse<T> {
statusCode: StatusCode.OK;
data: T;
}
interface IErrorResponse {
statusCode: Exclude<StatusCode, StatusCode.OK>;
data: undefined;
}
export type ApiResponse<T> = IOkResponse<T> | IErrorResponse;
Эта функция, которая отвечает за создание экземпляра моего ApiResponse<T>
объекта, работает нормально, потому что TypeScript будет неявно приведен number
к StatusCode
.
async function parseResponse<T>(response: Response): Promise<ApiResponse<T>> {
const text = await response.text();
const statusCode = response.status;
if (statusCode === StatusCode.OK) {
const data = JSON.parse(text) as T;
return { statusCode, data };
}
return { statusCode, data: undefined };
}
Теперь я могу использовать свой ApiResponse<T>
объект без ненужных неопределенных проверок, когда statusCode === 200
.
const ar1: ApiResponse<Test> = { statusCode: 200, data: { value: 5 } };
if (ar1.statusCode === StatusCode.OK) {
console.log(ar1.data.value);
}