我有一个联合类型Actions
type Actions = Readonly<{
type: ActionTypes.LOAD_POST;
payload: string;
}> | Readonly<{
type: ActionTypes.LOAD_POST_FAIL;
payload: string;
}> | Readonly<{
type: ActionTypes.LOAD_POST_SUCCESS;
payload: {
url: string;
post: Post;
};
}>
(这是生成的类型,原始版本嵌套了多个类型和ReturnType。)ActionTypes是一个字符串枚举。
const postReducer = (state = initialPostState, action: Actions): PostState => {
const { type, payload } = action;
switch (action.type) {
case ActionTypes.LOAD_POST_SUCCESS: {
const { post } = action.payload; // No error
return { ...state, loading: false, success: true, post };
}
}
switch (type) {
case ActionTypes.LOAD_POST: {
return { ...state, loading: true };
}
case ActionTypes.LOAD_POST_SUCCESS: {
// [ts] Type 'string | { url: string; post: IFullPost; }' has no property 'post' and no string index signature.
const { post } = payload;
return { ...state, loading: false, success: true, post };
}
case ActionTypes.LOAD_POST_FAIL: {
return { ...state, loading: false, success: false, post: null };
}
default:
return state;
}
};
为什么第一个工作而不是第二个?
您必须打开action.type
才能让action.payload
在case语句中更改其类型。
这是设计的。这是一个非常简单的例子:
type Actions =
{
type: 1,
payload: string;
} |
{
type: 2,
payload: { a: string }
}
function r(action: Actions) {
const { type } = action;
switch (type) {
case 2: {
// Type 'string | { a: string; }' has no property 'a' and no string index signature.
const { a } = action.payload;
}
}
}
当我们破坏action
对象:const { type, payload } = action;
时,我们丢失了被破坏类型的耦合信息。在此之后,type
常数将具有1 | 2
类型,payload
将具有string | { a: string; }
,即每种类型将基于Actions
类型结合所有可能的选项。这就是为什么TS无法弄清楚payload
的确切类型,因为在switch
条件下我们有绝对独立的变量。
您正在体验TypeScript达到type guards类型推断的极限。
在你的非工作示例中,TypeScript无法推断有关已经被破坏的变量payload
的任何信息,尽管它在技术上是可行的。我猜类型防护只能直接/字面上涉及防护表达的对象。