我有一个函数来调用一个返回序列化结果的web套接字,然后对其进行反序列化。反序列化(使用msgpack(的结果类型为unknown
。
因此,我想为调用函数提供一个泛型类型参数,以便将结果的类型传递给调用函数。
我的尝试是:
function call<T>(response: unknown): T {
return response; // Type 'unknown' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'. (2322)
}
TS游乐场
我可以限制泛型类型,但这是显示我问题的最简单的复制。
有没有一种方法可以通过泛型类型参数实现这一点,或者有没有更好的方法可以提供传入预期类型的可能性?
正如其中一条注释所指出的,使您的示例使用该签名的唯一方法是使用断言。所以,只需将您的返回更改为return response as T
即可。
但我认为有一种更好的方法来处理这种情况,即计算反序列化响应的返回类型。
我从来都不喜欢使用类型参数来告诉函数在类型为unknown
时返回什么类型的解决方案,因为它来自某些外部源,例如API调用。你基本上是在告诉编译器我知道我肯定会得到这种类型。我想在某些情况下这可能是真的,但在API调用中,我认为更可能的情况是,你知道你会得到什么,但你不能100%确定。毕竟,API可能会更改,服务器可能会出现错误,等等
我认为更好的方法是使用用户定义的类型保护来实际检查类型是什么。这会给你正确的类型和真正的类型安全。
最简单的方法是将返回类型保持为unknown
,然后将其传递给函数外的类型保护。因此,例如,假设您期望返回类型为Foo
:
function isFoo(response: unknown): response is Foo {
// code that checks the form of the response and
// returns true if valid
}
function call(response: unknown):unknown {
return response;
}
const myResponse
const myReturnedResponse = call(myResponse)
if(isFoo(myReturnedResponse)) {
//normal execution
} else {
// error handling
}
但你可以做得更好。您可以保留call
函数,但不是通过类型参数提供有关预期类型的信息,而是实际将typeguard本身作为运行时参数传入。这将使您保留上面的基本签名,该签名以未知参数开头,并返回类型化响应,但现在您拥有了真正的类型安全性。
所以——
function call<T>(response:unknown, isCorrectType: (x:unknown)=>x is T):T {
if(isCorrectType(response)) {
return response
} else {
throw("Invalid response")
}
}
请注意,这里仍然有一个泛型类型参数,但您不需要显式地将其放入(实际上只是一个断言(,而是从传入的类型保护的返回类型中推断它。
这是一个在操场上工作的例子。