我有一个工厂,应该根据提供的枚举键返回一个函数。 这是在切换情况下完成的,那里的所有功能都有不同的有效负载。
我要实现的目标是为该函数提供此类输入,以隐式验证所有有效负载类型和工厂的返回类型。参见下面的示例:
// the list of keys that are used for creation
enum keys {
key1 = 1,
key2 = 2
}
// here are interfaces, that tie each key with payload, that should be provided.
// Actually, payload is way more complex (another interfaces) but let's keep it simple
interface Test1 {
key: keys.key1;
payload: string;
}
interface Test2 {
key: keys.key2;
payload: number;
}
// this is the type, that comes to the function below
type tests = Test1 | Test2;
interface ReturnTypeOfTest1 { returnedObject1: number; }
interface ReturnTypeOfTest2 { returnedObject2: string; }
// this is how I'm configuring what should be returned depending on the key
// the return type is set to "ResourceModel", which infers a key from a function payload
// and identifies what exactly it should return
interface ModelOfTest {
[keys.key1]: ReturnTypeOfTest1;
[keys.key2]: ReturnTypeOfTest2;
}
type ResourceModel<R extends keys> = ModelOfTest[R];
ResourceModel
类型是根据另一个stackoverflow问题Typescript: Return type of function based on input value (enum)
使用上面的类型,我可以验证有效负载的类型,但可以不验证返回类型:
function getTest(t: tests): any{
switch (t.key) {
case keys.key1: {
const data = t.payload; // is string
return {returnedObject1: 123 };
} case keys.key2: {
const data = t.payload; // is number
return {returnedObject2: '123' };
}
}
}
getTest({key: keys.key1, payload: '1' }); // ReturnTypeOfTest1 | ReturnTypeOfTest2
或者获得适当的返回类型,但是在开关盒内放一个验证:
function getTest<T extends tests>(t: T): ResourceModel<T['key']> {
switch (t.key) {
case keys.key1: {
const d = t.payload; // string | number
return {returnedObject1: 123}; // also an error here, because it wants me to return both ReturnTypeOfTest1 & ReturnTypeOfTest2
} case keys.key2: {
const d = t.payload; // string | number
return null;
}
}
}
getTest({key: keys.key2, payload: 1 }); // ReturnTypeOfTest2
是否可以正确键入此内容?非常感谢您提供任何帮助。
答案 0 :(得分:1)
这本质上是missing feature in TypeScript (see microsoft/TypeScript#13995);编译器无法很好地说明未指定的泛型类型参数,例如T
在getTest()
的实现内部。当您呼叫 getTest()
时,将指定T
,然后正确计算返回类型。但是,当您实现 getTest()
时,T
仍然是一个未指定/无法解析的类型参数,编译器实际上会放弃。具体来说,它不会尝试使用control-flow-based type analysis根据T extends Test1 | Test2
的值将Test1
缩小为Test2
或t.key
。这意味着目前,这样的通用类型签名对函数调用者比对函数实现者有用。
也许有一天,这种语言会找到解决方案。目前,有解决方法。在这种情况下,我最常使用的方法是向该函数添加一个overload signature。让调用者看到对他们有用的通用重载签名,而实现则看到对它有用的非通用联合类型签名:
// call signature, seen by callers
function getTest<T extends tests>(t: T): ResourceModel<T['key']>;
// implementation signature, seen only by implementation
function getTest(t: tests): ResourceModel<keys> {
switch (t.key) {
case keys.key1: {
const d = t.payload; // string
const ret: ResourceModel<keys.key1> = { returnedObject1: 123 };
return ret;
} case keys.key2: {
const d = t.payload; // number
const ret: ResourceModel<keys.key2> = { returnedObject2: '123' };
return ret;
}
}
}
这仍然不是真正的类型安全;如果您切换return
语句,使ReturnTypeOfTest1
返回Test2
,反之亦然,以上内容将不会引起您的抱怨。但这至少是目前为止我们在这里能做的最好的,所以您在执行时需要小心。
让我们确保通话能够按需进行:
getTest({ key: keys.key1, payload: "1" }); // ReturnTypeOfTest1
getTest({ key: keys.key2, payload: 1 }); // ReturnTypeOfTest2
看起来不错。无论如何,我希望这会有所帮助;祝你好运!