Ограничения на количество пользователей, использующих typescript

#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);
}