Skip to content

Commit

Permalink
Add enhanced typings (#1287)
Browse files Browse the repository at this point in the history
* Add enhanced typings

* Cleanup

* Import JSX, whoops

* Refactor Events definition; add support for linkEvent

* Remove fixed TODO

* Lots of cleanup, also address #1295

* Clean up WrapperComponent

#1287 (comment)

* Remove global.d.ts
  • Loading branch information
as-com authored and Havunen committed Mar 17, 2018
1 parent 8a178e4 commit d7f0408
Show file tree
Hide file tree
Showing 14 changed files with 3,560 additions and 74 deletions.
8 changes: 7 additions & 1 deletion packages/inferno-compat/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,13 @@ class PureComponent<P, S> extends Component<P, S> {
}
}

class WrapperComponent<P, S> extends Component<P, S> {
interface ContextProps {
context: any;
}

type WrapperComponentProps<P> = P & ContextProps;

class WrapperComponent<P, S> extends Component<WrapperComponentProps<P>, S> {
public getChildContext() {
// tslint:disable-next-line
return this.props.context;
Expand Down
4 changes: 2 additions & 2 deletions packages/inferno-create-element/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function createElement<T>(type: string | Function | Component<any, any>,
let children: any = _children;
let ref: any = null;
let key = null;
let className = null;
let className: string | null = null;
let flags = 0;
let newProps;

Expand All @@ -44,7 +44,7 @@ export function createElement<T>(type: string | Function | Component<any, any>,

for (const prop in props) {
if (prop === 'className' || prop === 'class') {
className = props[prop];
className = (props as any)[prop];
} else if (prop === 'key') {
key = props.key;
} else if (prop === 'children' && isUndefined(children)) {
Expand Down
6 changes: 3 additions & 3 deletions packages/inferno-router/src/Link.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { linkEvent, createVNode, VNode } from 'inferno';
import { linkEvent, createVNode, VNode, MouseEvent } from 'inferno';
import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags';
import { invariant } from './utils';

const isModifiedEvent = (event): boolean => Boolean(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
const isModifiedEvent = (event: MouseEvent<any>): boolean => Boolean(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);

export interface ILinkProps {
children: any;
Expand All @@ -14,7 +14,7 @@ export interface ILinkProps {
innerRef: any;
}

function handleClick({ props, context }, event) {
function handleClick({ props, context }, event: MouseEvent<any>) {
if (props.onClick) {
props.onClick(event);
}
Expand Down
51 changes: 33 additions & 18 deletions packages/inferno-router/src/Route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import { Component, createComponentVNode, VNode } from 'inferno';
import { Component, ComponentType, createComponentVNode, InfernoChildren, VNode } from 'inferno';
import { VNodeFlags } from 'inferno-vnode-flags';
import { Children, invariant, warning } from './utils';
import { matchPath } from './matchPath';
import * as H from 'history';

const isEmptyChildren = children => Children.count(children) === 0;

export interface Match<P> {
params: P;
isExact: boolean;
path: string;
url: string;
}

export interface RouteComponentProps<P> {
match: Match<P>;
location: H.Location;
history: H.History;
staticContext?: any;
}

export interface IRouteProps {
computedMatch: any; // private, from <Switch>
path: any;
exact: any;
strict: any;
sensitive: any;
component: any;
render: any;
location: any;
children: Array<Component<any, any>>;
computedMatch?: any; // private, from <Switch>
path?: string;
exact?: boolean;
strict?: boolean;
sensitive?: boolean;
component?: ComponentType<RouteComponentProps<any>> | ComponentType<any>;
render?: ((props: RouteComponentProps<any>) => VNode);
location?: H.Location;
children: ((props: RouteComponentProps<any>) => VNode) | InfernoChildren;
}

/**
Expand All @@ -40,18 +55,18 @@ class Route extends Component<IRouteProps, any> {
};
}

public computeMatch({ computedMatch, location, path, strict, exact, sensitive }, router) {
public computeMatch({computedMatch, location, path, strict, exact, sensitive}, router) {
if (computedMatch) {
// <Switch> already computed the match for us
return computedMatch;
}

invariant(router, 'You should not use <Route> or withRouter() outside a <Router>');

const { route } = router;
const {route} = router;
const pathname = (location || route.location).pathname;

return path ? matchPath(pathname, { path, strict, exact, sensitive }) : route.match;
return path ? matchPath(pathname, {path, strict, exact, sensitive}) : route.match;
}

public componentWillReceiveProps(nextProps, nextContext) {
Expand All @@ -73,11 +88,11 @@ class Route extends Component<IRouteProps, any> {
}

public render(): VNode | null {
const { match } = this.state;
const { children, component, render } = this.props;
const { history, route, staticContext } = this.context.router;
const {match} = this.state;
const {children, component, render} = this.props;
const {history, route, staticContext} = this.context.router;
const location = this.props.location || route.location;
const props = { match, location, history, staticContext };
const props = {match, location, history, staticContext};

if (component) {
return match ? createComponentVNode(VNodeFlags.ComponentUnknown, component, props) : null;
Expand All @@ -100,7 +115,7 @@ class Route extends Component<IRouteProps, any> {
}

if (process.env.NODE_ENV !== 'production') {
Route.prototype.componentWillMount = function() {
Route.prototype.componentWillMount = function () {
warning(
!(this.props.component && this.props.render),
'You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored'
Expand Down
5 changes: 3 additions & 2 deletions packages/inferno-router/src/Switch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Component, createComponentVNode, VNode } from 'inferno';
import { matchPath } from './matchPath';
import { Children, invariant, isValidElement, warning } from './utils';
import { combineFrom } from 'inferno-shared';
import { IRouteProps } from "./Route";

export interface ISwitchProps {
export interface ISwitchProps extends IRouteProps {
router: any;
children: Array<Component<any, any>>;
// children: Array<Component<any, any>>;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions packages/inferno-router/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function invariant(condition, format, a?, b?, c?, d?, e?, f?) {
const ARR = [];

export const Children = {
forEach(children: any[], fn: Function): void {
forEach(children: any, fn: Function): void {
if (isNullOrUndef(children)) {
return;
}
Expand All @@ -52,19 +52,19 @@ export const Children = {
}
},

count(children: any[]): number {
count(children: any): number {
return Children.toArray(children).length;
},

only(children: any[]): any {
only(children: any): any {
children = Children.toArray(children);
if (children.length !== 1) {
throw new Error('Children.only() expects only one child.');
}
return children[0];
},

toArray(children: any[]): any[] {
toArray(children: any): any[] {
return isNullOrUndef(children) ? [] : isArray(children) ? children : ARR.concat(children);
}
};
2 changes: 1 addition & 1 deletion packages/inferno-router/src/withRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function withRouter(Com) {
const C: any = function(props: IWithRouterProps) {
const { wrappedComponentRef, ...remainingProps } = props;

return createComponentVNode(VNodeFlags.ComponentClass, Route, {
return createComponentVNode<any>(VNodeFlags.ComponentClass, Route, {
render(routeComponentProps) {
return createComponentVNode(
VNodeFlags.ComponentUnknown,
Expand Down
3 changes: 2 additions & 1 deletion packages/inferno/src/DOM/events/delegation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isNull } from 'inferno-shared';
import { SemiSyntheticEvent } from "inferno";

const attachedEventCounts = {};
const attachedEvents = {};
Expand Down Expand Up @@ -32,7 +33,7 @@ export function handleEvent(name: string, nextEvent: Function | null, dom) {
}
}

function dispatchEvents(event, target, isClick: boolean, name: string, eventData: IEventData) {
function dispatchEvents(event: SemiSyntheticEvent<any>, target, isClick: boolean, name: string, eventData: IEventData) {
let dom = target;
while (!isNull(dom)) {
// Html Nodes can be nested fe: span inside button in that scenario browser does not handle disabled attribute on parent,
Expand Down
61 changes: 61 additions & 0 deletions packages/inferno/src/DOM/events/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// Event System
// ----------------------------------------------------------------------

import {
NativeAnimationEvent, NativeClipboardEvent, NativeCompositionEvent, NativeDragEvent, NativeFocusEvent,
NativeKeyboardEvent,
NativeMouseEvent, NativeTouchEvent, NativeTransitionEvent, NativeUIEvent, NativeWheelEvent
} from "../../JSX";
import { LinkedEvent } from "./linkEvent";

export interface SemiSyntheticEvent<T> extends Event {
/**
* A reference to the element on which the event listener is registered.
*/
currentTarget: EventTarget & T;
}

export type ClipboardEvent<T> = SemiSyntheticEvent<T> & NativeClipboardEvent;
export type CompositionEvent<T> = SemiSyntheticEvent<T> & NativeCompositionEvent;
export type DragEvent<T> = MouseEvent<T> & NativeDragEvent;
export type FocusEvent<T> = SemiSyntheticEvent<T> & NativeFocusEvent;
export type FormEvent<T> = SemiSyntheticEvent<T>;

export interface InvalidEvent<T> extends SemiSyntheticEvent<T> {
target: EventTarget & T;
}

export interface ChangeEvent<T> extends SemiSyntheticEvent<T> {
target: EventTarget & T;
}

export type KeyboardEvent<T> = SemiSyntheticEvent<T> & NativeKeyboardEvent;
export type MouseEvent<T> = SemiSyntheticEvent<T> & NativeMouseEvent;
export type TouchEvent<T> = SemiSyntheticEvent<T> & NativeTouchEvent;
export type UIEvent<T> = SemiSyntheticEvent<T> & NativeUIEvent;
export type WheelEvent<T> = MouseEvent<T> & NativeWheelEvent;
export type AnimationEvent<T> = SemiSyntheticEvent<T> & NativeAnimationEvent;
export type TransitionEvent<T> = SemiSyntheticEvent<T> & NativeTransitionEvent;

//
// Event Handler Types
// ----------------------------------------------------------------------

export type EventHandler<E extends SemiSyntheticEvent<any>> = { bivarianceHack(event: E): void }["bivarianceHack"] | LinkedEvent<any, E>;

export type InfernoEventHandler<T> = EventHandler<SemiSyntheticEvent<T>>;

export type ClipboardEventHandler<T> = EventHandler<ClipboardEvent<T>>;
export type CompositionEventHandler<T> = EventHandler<CompositionEvent<T>>;
export type DragEventHandler<T> = EventHandler<DragEvent<T>>;
export type FocusEventHandler<T> = EventHandler<FocusEvent<T>>;
export type FormEventHandler<T> = EventHandler<FormEvent<T>>;
export type ChangeEventHandler<T> = EventHandler<ChangeEvent<T>>;
export type KeyboardEventHandler<T> = EventHandler<KeyboardEvent<T>>;
export type MouseEventHandler<T> = EventHandler<MouseEvent<T>>;
export type TouchEventHandler<T> = EventHandler<TouchEvent<T>>;
export type UIEventHandler<T> = EventHandler<UIEvent<T>>;
export type WheelEventHandler<T> = EventHandler<WheelEvent<T>>;
export type AnimationEventHandler<T> = EventHandler<AnimationEvent<T>>;
export type TransitionEventHandler<T> = EventHandler<TransitionEvent<T>>;
7 changes: 6 additions & 1 deletion packages/inferno/src/DOM/events/linkEvent.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { isFunction } from 'inferno-shared';

export interface LinkedEvent<T, E extends Event> {
data: T;
event: (data: T, event: E) => void;
}

/**
* Links given data to event as first parameter
* @param {*} data data to be linked, it will be available in function as first parameter
* @param {Function} event Function to be called when event occurs
* @returns {{data: *, event: Function}}
*/
export function linkEvent(data, event) {
export function linkEvent<T, E extends Event>(data: T, event: (data: T, event: E) => void): LinkedEvent<T, E> | null {
if (isFunction(event)) {
return { data, event };
}
Expand Down
Loading

0 comments on commit d7f0408

Please sign in to comment.