Skip to content

Commit

Permalink
Merge pull request #50 from marlon-tucker/Generics
Browse files Browse the repository at this point in the history
Fixes typescript generics to be correct for the returned proxy object
  • Loading branch information
Aaronius committed May 25, 2020
2 parents 47a4ba4 + 97f10b2 commit 7ec75fb
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 8 deletions.
8 changes: 4 additions & 4 deletions src/child/connectToParent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createDestructor from '../createDestructor';
import createLogger from '../createLogger';
import { SynMessage, Methods, PenpalError, CallSender } from '../types';
import { SynMessage, Methods, PenpalError, CallSender, AsyncMethodReturns } from '../types';
import { ErrorCode, MessageType, NativeEventType } from '../enums';
import validateWindowIsIframe from './validateWindowIsIframe';
import handleSynAckMessageFactory from './handleSynAckMessageFactory';
Expand Down Expand Up @@ -39,7 +39,7 @@ type Connection<TCallSender extends object = CallSender> = {
/**
* A promise which will be resolved once a connection has been established.
*/
promise: Promise<TCallSender>;
promise: Promise<AsyncMethodReturns<TCallSender>>;
/**
* A method that, when called, will disconnect any messaging channels.
* You may call this even before a connection has been established.
Expand Down Expand Up @@ -73,7 +73,7 @@ export default <TCallSender extends object = CallSender>(options: Options = {}):
window.parent.postMessage(synMessage, parentOriginForSyn);
};

const promise: Promise<TCallSender> = new Promise((resolve, reject) => {
const promise: Promise<AsyncMethodReturns<TCallSender>> = new Promise((resolve, reject) => {
const stopConnectionTimeout = startConnectionTimeout(timeout, destroy);
const handleMessage = (event: MessageEvent) => {
// Under niche scenarios, we get into this function after
Expand All @@ -92,7 +92,7 @@ export default <TCallSender extends object = CallSender>(options: Options = {}):
}

if (event.data.penpal === MessageType.SynAck) {
const callSender = handleSynAckMessage(event) as TCallSender;
const callSender = handleSynAckMessage(event) as AsyncMethodReturns<TCallSender>;
if (callSender) {
window.removeEventListener(NativeEventType.Message, handleMessage);
stopConnectionTimeout();
Expand Down
8 changes: 4 additions & 4 deletions src/parent/connectToChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import getOriginFromSrc from './getOriginFromSrc';
import createLogger from '../createLogger';
import handleSynMessageFactory from './handleSynMessageFactory';
import handleAckMessageFactory from './handleAckMessageFactory';
import { CallSender, Methods, PenpalError } from '../types';
import { CallSender, Methods, PenpalError, AsyncMethodReturns } from '../types';
import { ErrorCode, MessageType, NativeEventType } from '../enums';
import validateIframeHasSrcOrSrcDoc from './validateIframeHasSrcOrSrcDoc';
import monitorIframeRemoval from './monitorIframeRemoval';
Expand Down Expand Up @@ -39,7 +39,7 @@ type Connection<TCallSender extends object = CallSender> = {
/**
* A promise which will be resolved once a connection has been established.
*/
promise: Promise<TCallSender>;
promise: Promise<AsyncMethodReturns<TCallSender>>;
/**
* A method that, when called, will disconnect any messaging channels.
* You may call this even before a connection has been established.
Expand Down Expand Up @@ -80,7 +80,7 @@ export default <TCallSender extends object = CallSender>(options: Options): Conn
log
);

const promise: Promise<TCallSender> = new Promise((resolve, reject) => {
const promise: Promise<AsyncMethodReturns<TCallSender>> = new Promise((resolve, reject) => {
const stopConnectionTimeout = startConnectionTimeout(timeout, destroy);
const handleMessage = (event: MessageEvent) => {
if (event.source !== iframe.contentWindow || !event.data) {
Expand All @@ -93,7 +93,7 @@ export default <TCallSender extends object = CallSender>(options: Options): Conn
}

if (event.data.penpal === MessageType.Ack) {
const callSender = handleAckMessage(event) as TCallSender;
const callSender = handleAckMessage(event) as AsyncMethodReturns<TCallSender>;

if (callSender) {
stopConnectionTimeout();
Expand Down
16 changes: 16 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,19 @@ export type CallSender = {
* A Penpal-specific error.
*/
export type PenpalError = Error & { code: ErrorCode };

/**
* A mapped type to extract only object properties which are functions.
*/
export type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];

/**
* A mapped type to convert non async methods into async methods and exclude any non function properties.
*/
export type AsyncMethodReturns<T, K extends keyof T = FunctionPropertyNames<T>> = {
[KK in K]: T[KK] extends (...args: any[]) => PromiseLike<any>
? T[KK]
: T[KK] extends (...args: infer A) => infer R
? (...args: A) => Promise<R>
: T[KK]
};

0 comments on commit 7ec75fb

Please sign in to comment.