Skip to content

Commit

Permalink
Merge 4e27c88 into fbdb5e9
Browse files Browse the repository at this point in the history
  • Loading branch information
daybrush committed Oct 21, 2020
2 parents fbdb5e9 + 4e27c88 commit 5336ceb
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 33 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 47 additions & 31 deletions src/Component.ts
Expand Up @@ -4,18 +4,14 @@
*/

function isUndefined(value: any): boolean {
return typeof value === "undefined";
return typeof value === "undefined";
}

interface DefaultProps<T> {
eventType: string;
stop: () => void;
currentTarget: T;
}

type EventNoParamKey<T extends EventMap, K extends EventKey<T>> = T[K] extends NoArguments
? K
: never;
type NotFunction = { [k: string]: unknown } & ({ bind?: never } | { call?: never });
type NoArguments = undefined | null | void | never;
type EventWithRestParam = ((evt: NotFunction, ...restParam: any[]) => any);
Expand Down Expand Up @@ -45,25 +41,47 @@ type EventMap = Record<string, EventDefinition>;
type EventKey<T extends EventMap> = string & keyof T;
type EventHash<T extends EventMap, S> = Partial<{ [K in EventKey<T>]: EventCallback<T, K, S> }>;


type EventCallbackFirstParam<P, S> = P extends NoArguments ? DefaultProps<S> : P & DefaultProps<S>;
type EventCallbackFunction<T extends (...params: any[]) => any, S>
= T extends (firstParam?: infer F, ...restParams: infer R) => any
? (firstParam: EventCallbackFirstParam<Required<F>, S>, ...restParams: R) => any
: (firstParam: DefaultProps<S>) => any;


// In the on and once methods, the defaultProps must be included in the first parameter.
type EventCallback<T extends EventMap, K extends EventKey<T>, S>
= T[K] extends NoArguments
? (event: DefaultProps<S>) => any
: T[K] extends (evt: infer U, ...restParam: infer V) => any
? (event: U & DefaultProps<S>, ...restParam: V) => any
: (event: T[K] & DefaultProps<S>) => any;
type FirstParam<T extends EventMap, K extends EventKey<T>>
= T[K] extends NoArguments
? void
: T[K] extends (evt: infer U, ...restParam: any[]) => any
? U
: T[K];
type RestParam<T extends EventMap, K extends EventKey<T>>
= T[K] extends (evt: NotFunction, ...restParam: infer U) => any
? U
: void[]
= T[K] extends (...params: any[]) => any
? EventCallbackFunction<T[K], S>
: (event: EventCallbackFirstParam<T[K], S>) => any;

type EventTriggerFirstParam<T extends {}> = Pick<T, Exclude<keyof T, keyof DefaultProps<any>>> & Partial<DefaultProps<any>>;
type EventTriggerPartialFunction<F, R extends any[]>
= F extends undefined
? (firstParam?: EventTriggerFirstParam<F>, ...restParams: R) => any
: (firstParam: EventTriggerFirstParam<F>, ...restParams: R) => any


type EventTriggerFunction<T extends (...params: any[]) => any>
= T extends (firstParam: infer F, ...restParams: infer R) => any
? EventTriggerPartialFunction<F, R>
: (firstParam?: { [key: string]: never }) => any;

type EventTriggerNoFunction<T>
= T extends NoArguments
? (firstParam?: { [key: string]: never }) => any
: EventTriggerFunction<(fisrtParam: EventTriggerFirstParam<T>) => any>;

// You don't need to include defaultProps in the trigger method's first parameter.
type EventTriggerParams<T extends EventMap, K extends EventKey<T>>
= Parameters<T[K] extends (...params: any[]) => any
? EventTriggerFunction<T[K]>
: EventTriggerNoFunction<T[K]>>;



interface DefaultEventMap {
[key: string]: { [key: string]: any } | void;
[key: string]: (firstParam?: { [key: string]: any }, ...restParams: any[]) => any;
}

/**
Expand All @@ -87,8 +105,8 @@ class Component<T extends EventMap = DefaultEventMap> {
* @deprecated
* @private
*/
public options: {[key: string]: any} = {};
private _eventHandler: {[keys: string]: EventCallback<T, EventKey<T>, Component<T>>[]};
public options: { [key: string]: any } = {};
private _eventHandler: { [keys: string]: EventCallback<T, EventKey<T>, Component<T>>[] };

/**
* @support {"ie": "7+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.1+ (except 3.x)"}
Expand All @@ -97,8 +115,7 @@ class Component<T extends EventMap = DefaultEventMap> {
this._eventHandler = {};
}

public trigger<K extends EventKey<T>>(eventName: EventNoParamKey<T, K>): boolean;
public trigger<K extends EventKey<T>>(eventName: K, customEvent: FirstParam<T, K>, ...restParam: RestParam<T, K>): boolean;
public trigger<K extends EventKey<T>>(eventName: K, ...params: EventTriggerParams<T, K>): boolean;
/**
* Triggers a custom event.
* @ko 커스텀 이벤트를 발생시킨다
Expand Down Expand Up @@ -130,16 +147,15 @@ class Component<T extends EventMap = DefaultEventMap> {
* // https://github.com/naver/egjs-component/wiki/How-to-make-Component-event-design%3F
* ```
*/
public trigger<K extends EventKey<T>>(eventName: K, customEvent?: FirstParam<T, K>, ...restParam: any[]): boolean {
public trigger<K extends EventKey<T>>(eventName: K, ...params: any[]): boolean {
let handlerList = this._eventHandler[eventName] || [];
const hasHandlerList = handlerList.length > 0;

if (!hasHandlerList) {
return true;
}
if (!customEvent) {
customEvent = {} as any;
}
const customEvent = params[0] || {};
const restParams = params.slice(1);

// If detach method call in handler in first time then handler list calls.
handlerList = handlerList.concat();
Expand All @@ -153,8 +169,8 @@ class Component<T extends EventMap = DefaultEventMap> {

let arg: any[] = [customEvent];

if (restParam.length >= 1) {
arg = arg.concat(restParam);
if (restParams.length >= 1) {
arg = arg.concat(restParams);
}

handlerList.forEach(handler => {
Expand Down
34 changes: 33 additions & 1 deletion test/types/Component.test-d.ts
@@ -1,4 +1,4 @@
import Component from ".";
import Component from "../../src/Component";

const test = (testName: string, func: (...args: any[]) => any) => {}

Expand All @@ -13,6 +13,10 @@ class TestClass extends Component<{
c: boolean;
}) => any;
evt4: void;
evt5: {
a: 1;
stop(): void;
}
}> {
testMethod() {}
};
Expand Down Expand Up @@ -43,6 +47,10 @@ test("Correct event definitions", () => {
evt5: null;
evt6: undefined;
evt7: never;
evt8: {
a: 1,
stop(): void,
}
}> {};
});

Expand Down Expand Up @@ -81,8 +89,32 @@ test("Correct trigger() usage", () => {

// $ExpectType boolean
component.trigger("evt4");

// $ExpectType boolean
component.trigger("evt4", {});

// skip stop function
// $ExpectType boolean
component.trigger("evt5", {
a: 1,
});
});
test("Even if the event type is not set, there is no type error.", () => {
const defaultComponent = new Component();

// Any event passes.
defaultComponent.on("a", e => {
// $ExpectType any
e.a;
// $ExpectType any
e.b;
});

// Any parameters passes.
defaultComponent.trigger("a");
defaultComponent.trigger("a", { a: 1 });
defaultComponent.trigger("a", { a: 1 }, 1);
});
test("Correct on, once usage", () => {
["on", "once"].forEach((method: "on" | "once") => {
// $ExpectType TestClass
Expand Down

0 comments on commit 5336ceb

Please sign in to comment.