如下面的自包含代码片段(or here on Typescript Playground)所示,我想基于returnType
参数的action
道具在函数上强制执行返回类型。但是,我想证明returnType
是那个 returnType
的正确action
,而不仅仅是任何{{1} }。
向下滚动到代码段的底部,以了解我的意思:)
returnType
答案 0 :(得分:1)
TypeScript不会narrow type parameters通过类型保护。这意味着支票action.type === "type1"
确实缩小了action.type
,但没有缩小T
,因此返回类型仍然像联合类型void | {foo: "bar"}
。 @RyanCavanaugh said显然不是一个容易解决的问题:
“正确”的做法是显而易见的,但是需要对我们如何处理类型参数进行大量的修改,同时产生一致的负面[性能]影响,而对实际的面向用户的行为却没有相应的正面影响一侧。
因此您必须解决它。一种方法是在每个类型保护的子句中手动声明返回类型:
type Ret<T extends GameActionTypes> =
FindByTag<ActionType<gameActions>, {type: T}>["returnType"];
export function executeAction<T extends GameActionTypes>(
action: ActionType<gameActions>
): Ret<T> {
if (action.type === "type1") {
type R = Ret<typeof action.type>;
return undefined as R; // okay
} else if (action.type === "type2") {
type R = Ret<typeof action.type>;
return undefined as R; // error
}
}
请注意,每个受保护的子句中的本地类型别名R
不同,断言在一种情况下成功,而在另一种情况下失败。我不确定是否有人比这更容易使用类型安全的解决方案。
希望至少有一些帮助;祝你好运!
因此,我没有意识到action
参数不是通用的(我太关注实现内部的返回类型问题)。这意味着您有两个问题:在实现中推断返回值的正确类型,在调用函数时和推断返回值的正确类型。现在让我们解决后者。
首先,如果要基于函数参数推断不同的泛型类型,则还需要将该参数也设为泛型类型。最好的结果是当参数类型与通用类型参数相同(而不是类型参数的某些复杂函数)时。因此,让我们这样做:
export function executeAction<A extends ActionType<gameActions>>(
action: A
): A["returnType"] {
const actionUnion: ActionType<gameActions> = action; // remove generic
if (actionUnion.type === "type1") {
type R = Ret<typeof actionUnion.type>
return undefined as R;
} else if (action.type === "type2") {
type R = Ret<typeof actionUnion.type>
return undefined as R;
}
}
请注意,如何为操作指定类型A
,因此返回值仅为A['returnType']
。从呼叫者的角度来看,这现在非常简单,应该可以按您期望的方式工作:
declare const t1: TEST1;
const ret1 = executeAction(t1); // void
declare const t2: TEST2;
const ret2 = executeAction(t2); // {foo: "bar"}
该函数的实现需要进行一些调整...具体来说,泛型现在是动作类型A
,而不是动作类型T
type
属性。缩小甚至更不容易实现。解决方法是将action
分配给非泛型变量actionUnion
,这只是A
扩展的并集类型。然后,return undefined as Ret<typeof actionUnion.type>
的缩小与以前一样(令人沮丧)。
好的,希望也能有所帮助。再次祝你好运。