是否可以从传递给函数的参数中返回泛型类型

时间:2019-01-24 16:41:37

标签: typescript generics types type-safety nested-generics

我在下面彻底评论了代码。我觉得像这样理解问题要容易得多,而不是试图仅仅通过世界来解释它。

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 :(得分:2)

使用可以使用条件类型从EVENT中提取type参数。通常不会基于一个类型参数来推断类型参数,并且最终会得到最窄的类型(在这种情况下为{}

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

};