Можно ли вернуть универсальный тип из аргумента, переданного функции - PullRequest
0 голосов
/ 24 января 2019

Я подробно прокомментировал код ниже. Я чувствую, что гораздо проще понять эту проблему, чем пытаться объяснить ее только с помощью миров.

abstract class BaseEvent<REQUEST_PAYLOAD, RESPONSE_PAYLOAD> {
  constructor(public requestPayload: REQUEST_PAYLOAD) {}

  // this method was only created for the sake of showing response1 type example
  public return_RESPONSE_PAYLOAD_type(): RESPONSE_PAYLOAD {
    return null;
  }
}

type GetUserInfoRequest = { userId: number };
type GetUserInfoResponse = { username: string; age: number };

class GetUserInfoEvent extends BaseEvent<
  GetUserInfoRequest,
  GetUserInfoResponse
> {}

const emit = async <
  REQUEST_PAYLOAD,
  RESPONSE_PAYLOAD,
  EVENT extends BaseEvent<REQUEST_PAYLOAD, RESPONSE_PAYLOAD>
>(
  event: EVENT
): Promise<RESPONSE_PAYLOAD> => {
  // some stuff will be done there - for the sake of example it was removed
  return null;

  // return event.return_RESPONSE_PAYLOAD_type(); // doesn't work aswell
};

const main = async () => {
  const event = new GetUserInfoEvent({ userId: 666 });
  const response1 = event.return_RESPONSE_PAYLOAD_type(); // type === { username: string; age: number; }
  const response2 = await emit(event); // type === {} but I want it to be { username: string; age: number; }

  // response2.username <-- error
  // response2.age <-- error

  // I want emit function to return current RESPONSE_PAYLOAD type instead of an empty object. I don't want to manually cast returned types to GetUserInfoResponse because I want to achieve 100% type safety.
};

main();

Вероятно, мне не следует, чтобы он был протестирован с typescript@3.2.4.

1 Ответ

0 голосов
/ 24 января 2019

Использование может использовать условный тип для извлечения аргумента типа из EVENT.Параметры типа обычно не выводятся один на основе другого, и в итоге вы получаете самый узкий тип (в данном случае {})

abstract class BaseEvent<REQUEST_PAYLOAD, RESPONSE_PAYLOAD> {
    constructor(public requestPayload: REQUEST_PAYLOAD) { }

    // this method was only created for the sake of showing response1 type example
    public return_RESPONSE_PAYLOAD_type(): RESPONSE_PAYLOAD {
        return null;
    }
}

type GetUserInfoRequest = { userId: number };
type GetUserInfoResponse = { username: string; age: number };

class GetUserInfoEvent extends BaseEvent<
    GetUserInfoRequest,
    GetUserInfoResponse
    > { }
// Conditional type to extract the response payload type:
type RESPONSE_PAYLOAD<T extends BaseEvent<any, any>> = T extends BaseEvent<any, infer U> ? U : never; 
const emit = async <
    EVENT extends BaseEvent<any, any>
>(
    event: EVENT
): Promise<RESPONSE_PAYLOAD<EVENT>> => {
    // some stuff will be done there - for the sake of example it was removed
    return null;

    // return event.return_RESPONSE_PAYLOAD_type(); // doesn't work aswell
};

const main = async () => {
    const event = new GetUserInfoEvent({ userId: 666 });
    const response1 = event.return_RESPONSE_PAYLOAD_type(); // type === { username: string; age: number; }
    const response2 = await emit(event); // is now GetUserInfoResponse

    response2.username //<-- ok
    response2.age //<-- ok

};
...