interface MessageResponseType<R> {
  responseType: R;
}

interface MessageVariableType<V> {
  variableType: V;
}

type MessageType<R, V = undefined> = MessageResponseType<R> & MessageVariableType<V>;

type ResponseOf<T extends MessageResponseType<R>, R = unknown> = T['responseType'];
type VariableOf<T extends MessageVariableType<V>, V = unknown> = T['variableType'];

// ... 중간 생략

interface Message {
  currentUser: MessageType<UserObject | undefined>;
  userList: MessageType<UserObjectPage, UserSearchArgs>;
  // 이하 생략
}

// message는 Message 인터페이스의 필드명으로 제한된다.
// variable과 리턴 타입 역시 인터페이스에 설정한 값으로 제한된다.
function call<K extends keyof Message, R = ResponseOf<Message[K]>>(message: K, variable: VariableOf<Message[K]>): R {
  ...
}

// variable은 optional로 하지 않았기 때문에 이 경우에는 무조건 undefined를 넘겨줘야 한다.
// 리턴 값은 UserObject 또는 undefined
const user = call('currentUser', undefined);

// variable은 UserSearchArgs
// 리턴 값은 UserObjectPage
const list = call('userList', { ... });


사실 요건 맛뵈기에 불과함