打字稿:使用记录类型时出现类型错误
Typescript: Type Error while using Record type
对于对象中处理函数的以下代码(参见 Playground Link),我使用 Record 类型:
interface User {
id: string;
avatar: string;
email: string;
name: string;
role?: string;
[key: string]: any;
}
interface State {
isInitialized: boolean;
isAuthenticated: boolean;
user: User | null;
}
type InitializeAction = {
type: 'INITIALIZE';
payload: {
isAuthenticated: boolean;
user: User | null;
};
};
type LoginAction = {
type: 'LOGIN';
payload: {
user: User;
isAuthenticated: boolean;
};
};
type LogoutAction = {
type: 'LOGOUT';
};
type RegisterAction = {
type: 'REGISTER';
payload: {
user: User;
};
};
type Action =
| InitializeAction
| LoginAction
| LogoutAction
| RegisterAction;
const handlers: Record<string, (state: State, action: Action) => State> = {
INITIALIZE: (state: State, action: InitializeAction): State => {
const {
isAuthenticated,
user
} = action.payload;
return {
...state,
isAuthenticated,
isInitialized: true,
user
};
},
LOGIN: (state: State, action: LoginAction): State => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
},
LOGOUT: (state: State): State => ({
...state,
isAuthenticated: false,
user: null
}),
REGISTER: (state: State, action: RegisterAction): State => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
}
};
虽然处理函数出现以下错误:
TS2322: Type '(state: State, action: InitializeAction) => State' is not assignable to type '(state: State, action: Action) => State'.
Types of parameters 'action' and 'action' are incompatible.
Type 'Action' is not assignable to type 'InitializeAction'.
Type 'LoginAction' is not assignable to type 'InitializeAction'.
Types of property 'type' are incompatible.
Type '"LOGIN"' is not assignable to type '"INITIALIZE"'.
这有点脏,但它有效:
const handlers: Record<string, (state: State, action: Action) => State> = {
INITIALIZE: (state: State, action: Action): State => {
const { isAuthenticated, user } = (action as InitializeAction).payload;
return {
...state,
isAuthenticated,
isInitialized: true,
user,
};
},
LOGIN: (state: State, action: Action): State => {
const { user } = (action as LoginAction).payload;
return {
...state,
isAuthenticated: true,
user,
};
},
LOGOUT: (state: State): State => ({
...state,
isAuthenticated: false,
user: null,
}),
REGISTER: (state: State, action: Action): State => {
const { user } = (action as RegisterAction).payload;
return {
...state,
isAuthenticated: true,
user,
};
},
};
我相信这是因为逆变。 您可以找到有关此主题及其在打字稿中如何工作的更多信息。
为了键入您的 handler
对象,您应该使用 mapped types:
interface User {
id: string;
avatar: string;
email: string;
name: string;
role?: string;
[key: string]: any;
}
interface State {
isInitialized: boolean;
isAuthenticated: boolean;
user: User | null;
}
type InitializeAction = {
type: 'INITIALIZE';
payload: {
isAuthenticated: boolean;
user: User | null;
};
};
type LoginAction = {
type: 'LOGIN';
payload: {
user: User;
isAuthenticated: boolean;
};
};
type LogoutAction = {
type: 'LOGOUT';
};
type RegisterAction = {
type: 'REGISTER';
payload: {
user: User;
};
};
type Action =
| InitializeAction
| LoginAction
| LogoutAction
| RegisterAction;
// type Handlers = {
// INITIALIZE: (state: State, action: InitializeAction) => State;
// LOGIN: (state: State, action: LoginAction) => State;
// LOGOUT: (state: State, action: LogoutAction) => State;
// REGISTER: (state: State, action: RegisterAction) => State;
// }
type Handlers = {
[Type in Action['type']]: (state: State, action: Extract<Action, { type: Type }>) => State
}
const handlers: Handlers = {
INITIALIZE: (state, action) => {
const {
isAuthenticated,
user
} = action.payload;
return {
...state,
isAuthenticated,
isInitialized: true,
user
};
},
LOGIN: (state, action) => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
},
LOGOUT: (state) => ({
...state,
isAuthenticated: false,
user: null
}),
REGISTER: (state, action) => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
}
};
你可以找到相关的答案。
看这个例子:
type Reducer = (state: State, action: Action) => State;
const reducer: Reducer = (state, action) => state
type ContravariantReducer = Record<string, Reducer>
// Arrow of inheritance has changed in an opposite way
const contravariant: ContravariantReducer = {
initialize: (state: State, action: InitializeAction) => state
}
// Arrow of inheritance has changed in an opposite way
const contravariant2: Record<string, (state: State, action: InitializeAction) => State> = {
initialize: (state: State, action: Action) => state
}
P.S。除了 typescript 类型问题,我坚信您应该根据 @Dima Parzhitsky
提供的 redux 文档来键入您的 reducer
对于对象中处理函数的以下代码(参见 Playground Link),我使用 Record 类型:
interface User {
id: string;
avatar: string;
email: string;
name: string;
role?: string;
[key: string]: any;
}
interface State {
isInitialized: boolean;
isAuthenticated: boolean;
user: User | null;
}
type InitializeAction = {
type: 'INITIALIZE';
payload: {
isAuthenticated: boolean;
user: User | null;
};
};
type LoginAction = {
type: 'LOGIN';
payload: {
user: User;
isAuthenticated: boolean;
};
};
type LogoutAction = {
type: 'LOGOUT';
};
type RegisterAction = {
type: 'REGISTER';
payload: {
user: User;
};
};
type Action =
| InitializeAction
| LoginAction
| LogoutAction
| RegisterAction;
const handlers: Record<string, (state: State, action: Action) => State> = {
INITIALIZE: (state: State, action: InitializeAction): State => {
const {
isAuthenticated,
user
} = action.payload;
return {
...state,
isAuthenticated,
isInitialized: true,
user
};
},
LOGIN: (state: State, action: LoginAction): State => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
},
LOGOUT: (state: State): State => ({
...state,
isAuthenticated: false,
user: null
}),
REGISTER: (state: State, action: RegisterAction): State => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
}
};
虽然处理函数出现以下错误:
TS2322: Type '(state: State, action: InitializeAction) => State' is not assignable to type '(state: State, action: Action) => State'.
Types of parameters 'action' and 'action' are incompatible.
Type 'Action' is not assignable to type 'InitializeAction'.
Type 'LoginAction' is not assignable to type 'InitializeAction'.
Types of property 'type' are incompatible.
Type '"LOGIN"' is not assignable to type '"INITIALIZE"'.
这有点脏,但它有效:
const handlers: Record<string, (state: State, action: Action) => State> = {
INITIALIZE: (state: State, action: Action): State => {
const { isAuthenticated, user } = (action as InitializeAction).payload;
return {
...state,
isAuthenticated,
isInitialized: true,
user,
};
},
LOGIN: (state: State, action: Action): State => {
const { user } = (action as LoginAction).payload;
return {
...state,
isAuthenticated: true,
user,
};
},
LOGOUT: (state: State): State => ({
...state,
isAuthenticated: false,
user: null,
}),
REGISTER: (state: State, action: Action): State => {
const { user } = (action as RegisterAction).payload;
return {
...state,
isAuthenticated: true,
user,
};
},
};
我相信这是因为逆变。
为了键入您的 handler
对象,您应该使用 mapped types:
interface User {
id: string;
avatar: string;
email: string;
name: string;
role?: string;
[key: string]: any;
}
interface State {
isInitialized: boolean;
isAuthenticated: boolean;
user: User | null;
}
type InitializeAction = {
type: 'INITIALIZE';
payload: {
isAuthenticated: boolean;
user: User | null;
};
};
type LoginAction = {
type: 'LOGIN';
payload: {
user: User;
isAuthenticated: boolean;
};
};
type LogoutAction = {
type: 'LOGOUT';
};
type RegisterAction = {
type: 'REGISTER';
payload: {
user: User;
};
};
type Action =
| InitializeAction
| LoginAction
| LogoutAction
| RegisterAction;
// type Handlers = {
// INITIALIZE: (state: State, action: InitializeAction) => State;
// LOGIN: (state: State, action: LoginAction) => State;
// LOGOUT: (state: State, action: LogoutAction) => State;
// REGISTER: (state: State, action: RegisterAction) => State;
// }
type Handlers = {
[Type in Action['type']]: (state: State, action: Extract<Action, { type: Type }>) => State
}
const handlers: Handlers = {
INITIALIZE: (state, action) => {
const {
isAuthenticated,
user
} = action.payload;
return {
...state,
isAuthenticated,
isInitialized: true,
user
};
},
LOGIN: (state, action) => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
},
LOGOUT: (state) => ({
...state,
isAuthenticated: false,
user: null
}),
REGISTER: (state, action) => {
const { user } = action.payload;
return {
...state,
isAuthenticated: true,
user
};
}
};
看这个例子:
type Reducer = (state: State, action: Action) => State;
const reducer: Reducer = (state, action) => state
type ContravariantReducer = Record<string, Reducer>
// Arrow of inheritance has changed in an opposite way
const contravariant: ContravariantReducer = {
initialize: (state: State, action: InitializeAction) => state
}
// Arrow of inheritance has changed in an opposite way
const contravariant2: Record<string, (state: State, action: InitializeAction) => State> = {
initialize: (state: State, action: Action) => state
}
P.S。除了 typescript 类型问题,我坚信您应该根据 @Dima Parzhitsky
提供的 redux 文档来键入您的 reducer