Created
May 20, 2019 07:42
-
-
Save aprilrd/2d4e59eae3d63190f50595411e844832 to your computer and use it in GitHub Desktop.
Typescript+Redux Best Practice at Vingle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Connect { | |
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, State = {}>( | |
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>, | |
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>, | |
): InferableComponentEnhancerWithProps< | |
TStateProps & TDispatchProps & TOwnProps, | |
TOwnProps | |
>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Connect { | |
<TStateProps = {}, no_dispatch = {}, TOwnProps = {}, State = {}>( | |
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>, | |
): InferableComponentEnhancerWithProps< | |
TStateProps & DispatchProp<any> & TOwnProps, | |
TOwnProps | |
>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface DispatchProp<S> { | |
dispatch?: Dispatch<S>; | |
} | |
export interface InferableComponentEnhancerWithProps< | |
TInjectedProps, | |
TNeedsProps | |
> { | |
<P extends TInjectedProps>(component: Component<P>): ComponentClass< | |
Omit<P, keyof TInjectedProps> & TNeedsProps | |
> & { WrappedComponent: Component<P> }; | |
} | |
export type InferableComponentEnhancer< | |
TInjectedProps | |
> = InferableComponentEnhancerWithProps<TInjectedProps, {}>; | |
export interface Connect { | |
(): InferableComponentEnhancer<DispatchProp<any>>; | |
... | |
// this is an all-encompassing definition | |
// TStateProps is a type for props generated by mapStateToProps | |
// TDispatchProps is a type for props generated by mapDispatchToProps | |
// TOwnProps is a type for props from the container component's parent. | |
// TMergedProps is a type for props generated by merge. | |
// State is a type for Redux store. | |
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}, State = {}>( | |
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>, | |
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>, | |
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>, | |
options: Options<State, TStateProps, TOwnProps, TMergedProps> | |
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<P extends (TStateProps & DispatchProp<any> & TOwnProps)>(component: Component<P>): ComponentClass<Omit<P, keyof (TStateProps & DispatchProp<any> & TOwnProps)> & TOwnProps> & {WrappedComponent: Component<P>} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface InjectedIntlProps { | |
intl: InjectedIntl; | |
} | |
function injectIntl<P>( | |
component: ComponentConstructor<P & InjectedIntlProps>, | |
options?: InjectIntlConfig, | |
): React.ComponentClass<P> & { | |
WrappedComponent: ComponentConstructor<P & InjectedIntlProps>; | |
}; | |
// actual usage | |
interface IProps { | |
flag: boolean; | |
} | |
class Toast extends React.PureComponent<IProps & InjectedIntlProps> { | |
... | |
} | |
export default injectIntl<IProps>(Toast); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(initialState: State, ownProps: TOwnProps) => ( | |
state: State, | |
ownProps: TOwnProps, | |
) => TStateProps; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type SearchData = { query: string }; | |
type AppState = { | |
searchData: SearchData; | |
}; | |
type Props = { query: string; data: any; dispatch: Dispatch<any> }; | |
function mapStateToProps(state: AppState) { | |
return { | |
query: state.searchData.query, | |
}; | |
} | |
const Container = (_props: Props) => { | |
// render something and do something useful | |
return <div />; | |
}; | |
const A = connect(mapStateToProps)(Container); | |
<A data />; // this is valid | |
<A data dispatch={store.dispatch} />; // this isn't valid |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Container = (props: { data: any; dispatch: Dispatch<any> }) => { | |
// render something and do something useful | |
return <div />; | |
}; | |
export default connect()(Container); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type TStateProps = ReturnType<typeof mapStateToProps>; | |
type TOwnProps = Omit<Props, keyof TStateProps | keyof DispatchProp<any>>; // this results in { data: any }. But this isn't necessary and you can use {} without a problem. | |
const B = connect<TStateProps, {}, TOwnProps, AppState>(mapStateToProps)( | |
Container, | |
); | |
<B data />; // this is valid | |
<B data dispatch={store.dispatch} />; // this isn't valid |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum ActionTypes { | |
FETCH_USER = "FETCH_USER", | |
} | |
interface IFetchUserAction { | |
type: ActionTypes.FETCH_USER; | |
payload: { userId: string } | |
} | |
interface IOtherAction { | |
type: "____________________"; | |
} | |
type Actions = IFetchUserAction | IOtherAction; | |
function fetchUser(userId: string): IFetchUserAction { | |
return { | |
type: ActionTypes.FETCH_USER, | |
payload: { | |
userId, | |
} | |
}; | |
} | |
function reducer( | |
state = INITIAL_STATE, | |
action: Actions, | |
): IState { | |
switch (action.type) { | |
case ActionTypes.FETCH_USER: { | |
// in this closure, Typescript knows that action is of interface IFetchUserAction, thanks to enum ActionTypes. | |
return { | |
...state, | |
userId: action.payload.userId, | |
}; | |
} | |
default: { | |
return state | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ActionCreatorsMapObject } from "redux"; | |
// interface ActionCreatorsMapObject { | |
// [key: string]: ActionCreator<any>; | |
// } | |
type ActionUnion<T extends ActionCreatorsMapObject> = ReturnType< | |
T[keyof T] | |
>; | |
enum ActionTypes { | |
FETCH_USER = "FETCH_USER", | |
} | |
function createAction<T extends { type: ActionTypes }>(d: T): T { | |
return d; | |
} | |
export const ActionCreators = { | |
fetchUser(payload: {userId: string}) => | |
createAction({type: ActionTypes.FETCH_USER, payload}), | |
} | |
type Actions = ActionUnion<typeof ActionCreators>; | |
function reducer( | |
state = INITIAL_STATE, | |
action: Actions, | |
): IState { | |
switch (action.type) { | |
case ActionTypes.FETCH_USER: { | |
// in this closure, Typescript knows that action is of ActionCreators.fetchUser's ReturnType. | |
return { | |
...state, | |
userId: action.payload.userId, | |
}; | |
} | |
default: { | |
return state | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Container = (props: { data: any; dispatch: Dispatch<any> }) => { | |
// render something and do something useful | |
return <div />; | |
}; | |
const ConnectedContainer = connect()(Container); | |
describe("", () => { | |
let wrapper: ReactWrapper; | |
beforeEach(() => { | |
const store = mockStore(state); | |
wrapper = mount(<ConnectedContainer data dispatch={store.dispatch} />, { store }); | |
}); | |
... | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function reducer(state = INITIAL_STATE, action: Redux.Action) { | |
switch (action.type) { | |
case ActionTypes.FETCH_USER: { | |
// simple case | |
return { | |
...state, | |
userId: (action.payload as any).userId, | |
}; | |
} | |
default: { | |
return state; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
React.Component<P, S> | |
React.StatelessComponent<P> | |
React.ReactElement = instantiated React Component | |
React.ReactNode = React.ReactElement + Renderable primitive types (object is not valid). `children` has this type | |
React.CSSProperties | |
React.ReactEventHandler | |
React.<Input>Event | |
React.HTMLProps<ElementType> = Used to extend your component props. Ex) TOwnProps & React.HTMLProps<HTMLDivElment> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment