Javascript 使用React'理解Typescript记录类型;使用上下文

Javascript 使用React'理解Typescript记录类型;使用上下文,javascript,reactjs,typescript,react-context,Javascript,Reactjs,Typescript,React Context,我在理解为React上下文开发的生成器函数中的记录类型时遇到了一些问题 这是我的生成器函数: import * as React from 'react' export type State = Record<string, unknown> export type Action = { type: string; payload?: unknown } // eslint-disable-next-line @typescript-eslint/ban-types export

我在理解为React上下文开发的生成器函数中的记录类型时遇到了一些问题

这是我的生成器函数:

import * as React from 'react'

export type State = Record<string, unknown>
export type Action = { type: string; payload?: unknown }

// eslint-disable-next-line @typescript-eslint/ban-types
export type Actions = Record<string, Function>


export type AppContext = { state: State; actions: Actions }
export type Reducer = (state: State, action: Action) => State

type ProviderProps = Record<string, unknown>
type FullContext = {
    Context: React.Context<AppContext>
    Provider: React.FC<ProviderProps>
}

/**
 * Automates context creation
 *
 * @param {Reducer} reducer
 * @param {Actions} actions
 * @param {State} initialState
 * @returns {Contex, Provider}
 */
export default (
    reducer: Reducer,
    actions: Actions,
    initialState: State,
    init: () => State = (): State => initialState,
): FullContext => {
    const Context = React.createContext<AppContext>({
        state: { ...initialState },
        actions,
    })

    const Provider = ({ children }: { children?: React.ReactNode }) => {
        const [state, dispatch] = React.useReducer(reducer, initialState, init)

        const boundActions: Actions = {}
        for (const key in actions) {
            boundActions[key] = actions[key](dispatch)
        }

        return (
            <Context.Provider value={{ state, actions: { ...boundActions } }}>
                {children}
            </Context.Provider>
        )
    }

    return {
        Context,
        Provider,
    }
}

我想做的是使用reducer和inital状态调用一个函数,生成一个上下文提供程序,我可以在应用程序的任何地方使用它。在本例中,我试图生成一个
AuthContext
。此上下文将采用如下所示的减速器:

const authReducer: Reducer = (state: State, action: Action): State => {
    switch (action.type) {
        case 'login':
            return { ...state, isLogged: true, avoidLogin: false }
        case 'logout':
            return { ...state, isLogged: false, avoidLogin: true }
        default:
            return { ...state }
    }
}
非常简单,但它基本上改变了对象中的一些布尔值

现在,这段代码实际上可以工作了,但让我感到困扰的是在Actions类型声明中使用了函数类型

导出类型动作=记录

使用
unknown
也可以,但现在typescript抱怨
对象的类型为“unknown”。
在执行时:
boundActions[key]=actions[key](dispatch)
,特别是在
actions[key]
上,因为我将其作为函数调用

动作是以函数(分派)作为参数并返回
状态
对象的函数

以下是我的行动宣言,以进一步说明:

const actions: Actions = {
    login: (dispatch: (action: Action) => void) => () => {
        dispatch({ type: 'login' })
    },
    logout: (dispatch: (action: Action) => void) => () => {
        dispatch({ type: 'logout' })
    },
}
如果我在生成器中记录控制台日志
boundActions
,我会得到如下结果:

Object {
  "login": [Function anonymous],
  "logout": [Function anonymous],
}
Type 'Record<string, unknown>' is not assignable to type '(dispatch: Function) => Record<string, unknown>'.
  Type 'Record<string, unknown>' provides no match for the signature '(dispatch: Function): Record<string, unknown>'.
export default function Login(): JSX.Element {
    const {
        actions: { login },
    } = React.useContext(AuthContext)

    return (
        <View>
            <Button text='Entrar' onPress={() => login()} />
        </View>
    )
}
这正是我想要的,因为我希望能够在代码中的任何地方调用这些函数,而这将反过来调用特定减速机的dispatch函数来更改特定上下文的状态

现在,我对Typescript还相当陌生,但我的直觉告诉我
操作
类型的声明应该是这样的:

Object {
  "login": [Function anonymous],
  "logout": [Function anonymous],
}
Type 'Record<string, unknown>' is not assignable to type '(dispatch: Function) => Record<string, unknown>'.
  Type 'Record<string, unknown>' provides no match for the signature '(dispatch: Function): Record<string, unknown>'.
export default function Login(): JSX.Element {
    const {
        actions: { login },
    } = React.useContext(AuthContext)

    return (
        <View>
            <Button text='Entrar' onPress={() => login()} />
        </View>
    )
}
导出类型操作=记录状态>

这方面的问题是: 1-它不起作用,因为现在
boundActions
会说:

Object {
  "login": [Function anonymous],
  "logout": [Function anonymous],
}
Type 'Record<string, unknown>' is not assignable to type '(dispatch: Function) => Record<string, unknown>'.
  Type 'Record<string, unknown>' provides no match for the signature '(dispatch: Function): Record<string, unknown>'.
export default function Login(): JSX.Element {
    const {
        actions: { login },
    } = React.useContext(AuthContext)

    return (
        <View>
            <Button text='Entrar' onPress={() => login()} />
        </View>
    )
}
这实际上是可行的,但它不是类型安全的


谢谢。

如果您处理的操作接受操作创建者提供的不同参数,那么您希望基于这些特定操作的映射而不是模糊的通用类型来定义类型

由于此处只有两个动作创建者,它们都不接受任何参数(除dispatch之外),因此可以使用以下类型来描述它们:

(dispatch: React.Dispatch<Action>) => () => void
具体实施

export type AuthState = {
    isLogged: boolean;
    avoidLogin: boolean;
}

const authReducer = (state: AuthState, action: Action): AuthState => {
    switch (action.type) {
        case 'login':
            return { ...state, isLogged: true, avoidLogin: false }
        case 'logout':
            return { ...state, isLogged: false, avoidLogin: true }
        default:
            return { ...state }
    }
}

// no type = let it be inferred
const actions = {
    login: (dispatch: Dispatch) => () => {
        dispatch({ type: 'login' })
    },
    logout: (dispatch: Dispatch) => () => {
        dispatch({ type: 'logout' })
    },
}

const initialAuthState: AuthState = {
    isLogged: false,
    avoidLogin: true
}

const { Context: AuthContext, Provider } = createAuthContext(authReducer, actions, initialAuthState);

export function Login(): JSX.Element {
    // this is where the magic happens!
    const {
        // can only destructure known actions, will get errors on typos
        actions: { login },
        state
    } = React.useContext(AuthContext)

    // we now know all the properties of the state and their types
    const { isLogged } = state;

    return (
        <View>
            <Button text='Entrar' onPress={() => login()} />
        </View>
    )
}
导出类型AuthState={
isLogged:布尔值;
avoidLogin:布尔型;
}
常量authReducer=(状态:AuthState,操作:action):AuthState=>{
开关(动作类型){
案例“登录”:
返回{…状态,isLogged:true,avoidLogin:false}
“注销”案例:
返回{…状态,isLogged:false,avoidLogin:true}
违约:
返回{…状态}
}
}
//无类型=让它被推断
常量动作={
登录:(分派:分派)=>()=>{
分派({type:'login'})
},
注销:(发送:发送)=>()=>{
分派({type:'logout'})
},
}
常量initialAuthState:AuthState={
isLogged:错误,
阿维多金:没错
}
const{Context:AuthContext,Provider}=createAuthContext(authReducer,actions,initialAuthState);
导出函数Login():JSX.Element{
//这就是魔法发生的地方!
常数{
//只能对已知操作进行解构,否则会出现打字错误
操作:{login},
状态
}=React.useContext(AuthContext)
//我们现在知道了状态的所有属性及其类型
const{isLogged}=状态;
返回(
登录()}/>
)
}

如果您处理的操作接受操作创建者提供的不同参数,那么您希望基于这些特定操作的映射而不是模糊的通用类型来定义类型

由于此处只有两个动作创建者,它们都不接受任何参数(除dispatch之外),因此可以使用以下类型来描述它们:

(dispatch: React.Dispatch<Action>) => () => void
具体实施

export type AuthState = {
    isLogged: boolean;
    avoidLogin: boolean;
}

const authReducer = (state: AuthState, action: Action): AuthState => {
    switch (action.type) {
        case 'login':
            return { ...state, isLogged: true, avoidLogin: false }
        case 'logout':
            return { ...state, isLogged: false, avoidLogin: true }
        default:
            return { ...state }
    }
}

// no type = let it be inferred
const actions = {
    login: (dispatch: Dispatch) => () => {
        dispatch({ type: 'login' })
    },
    logout: (dispatch: Dispatch) => () => {
        dispatch({ type: 'logout' })
    },
}

const initialAuthState: AuthState = {
    isLogged: false,
    avoidLogin: true
}

const { Context: AuthContext, Provider } = createAuthContext(authReducer, actions, initialAuthState);

export function Login(): JSX.Element {
    // this is where the magic happens!
    const {
        // can only destructure known actions, will get errors on typos
        actions: { login },
        state
    } = React.useContext(AuthContext)

    // we now know all the properties of the state and their types
    const { isLogged } = state;

    return (
        <View>
            <Button text='Entrar' onPress={() => login()} />
        </View>
    )
}
导出类型AuthState={
isLogged:布尔值;
avoidLogin:布尔型;
}
常量authReducer=(状态:AuthState,操作:action):AuthState=>{
开关(动作类型){
案例“登录”:
返回{…状态,isLogged:true,avoidLogin:false}
“注销”案例:
返回{…状态,isLogged:false,avoidLogin:true}
违约:
返回{…状态}
}
}
//无类型=让它被推断
常量动作={
登录:(分派:分派)=>()=>{
分派({type:'login'})
},
注销:(发送:发送)=>()=>{
分派({type:'logout'})
},
}
常量initialAuthState:AuthState={
isLogged:错误,
阿维多金:没错
}
const{Context:AuthContext,Provider}=createAuthContext(authReducer,actions,initialAuthState);
导出函数Login():JSX.Element{
//这就是魔法发生的地方!
常数{
//只能对已知操作进行解构,否则会出现打字错误
操作:{login},
状态
}=React.useContext(AuthContext)
//我们现在知道了状态的所有属性及其类型
const{isLogged}=状态;
返回(
登录()}/>
)
}