将未知类型缩小为泛型类型参数
Narrow unknown type to generic type parameter
我有一个调用 Web 套接字的函数,该套接字 returns 序列化结果,然后反序列化。反序列化的结果类型(使用 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)
}
我可以限制通用类型,但这是显示我的问题的最简单的再现。
有没有一种方法可以使用泛型类型参数来实现这一点,或者有没有更好的方法来提供传递预期类型的可能性?
正如其中一条评论所指出的,使您的示例准确地使用该签名的唯一方法是使用断言。所以,只需将 return 更改为 return response as T
。
但我认为有更好的方法来处理这种情况,即找出反序列化响应的 return 类型。
我从来都不喜欢这样的解决方案,即使用类型参数来告诉函数 return 什么类型,否则该类型将是 unknown
,因为它来自某些外部来源,例如,一个API打电话。您基本上是在告诉编译器我 知道 我一定会取回这种类型。我想在某些情况下这可能是真的,但在 API 通话中,我认为更可能的情况是你知道你 可能 会得到什么,但你不是 100% 确定。毕竟,API 可能会发生变化,服务器可能会出现错误,等等。
我认为更好的方法是使用 user defined type guard 来实际检查您认为的类型。这为您提供了正确的类型和真正的类型安全。
最简单的方法是将 return 类型保留为 unknown
,然后将其传递给函数外部的类型保护。因此,例如,假设您期望 return 类型的 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
函数,但不是通过类型参数提供有关预期类型的信息,而是将类型保护本身作为运行时参数传递。这将使您保留上面的基本签名,该签名以未知参数和 return 类型的响应开头,但现在您拥有真正的类型安全。
所以--
function call<T>(response:unknown, isCorrectType: (x:unknown)=>x is T):T {
if(isCorrectType(response)) {
return response
} else {
throw("Invalid response")
}
}
注意这里还有一个泛型类型参数,但你不需要显式地把它放进去(这实际上只是一个断言),它是从类型保护的 return 类型推断出来的你通过了。
我有一个调用 Web 套接字的函数,该套接字 returns 序列化结果,然后反序列化。反序列化的结果类型(使用 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)
}
我可以限制通用类型,但这是显示我的问题的最简单的再现。
有没有一种方法可以使用泛型类型参数来实现这一点,或者有没有更好的方法来提供传递预期类型的可能性?
正如其中一条评论所指出的,使您的示例准确地使用该签名的唯一方法是使用断言。所以,只需将 return 更改为 return response as T
。
但我认为有更好的方法来处理这种情况,即找出反序列化响应的 return 类型。
我从来都不喜欢这样的解决方案,即使用类型参数来告诉函数 return 什么类型,否则该类型将是 unknown
,因为它来自某些外部来源,例如,一个API打电话。您基本上是在告诉编译器我 知道 我一定会取回这种类型。我想在某些情况下这可能是真的,但在 API 通话中,我认为更可能的情况是你知道你 可能 会得到什么,但你不是 100% 确定。毕竟,API 可能会发生变化,服务器可能会出现错误,等等。
我认为更好的方法是使用 user defined type guard 来实际检查您认为的类型。这为您提供了正确的类型和真正的类型安全。
最简单的方法是将 return 类型保留为 unknown
,然后将其传递给函数外部的类型保护。因此,例如,假设您期望 return 类型的 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
函数,但不是通过类型参数提供有关预期类型的信息,而是将类型保护本身作为运行时参数传递。这将使您保留上面的基本签名,该签名以未知参数和 return 类型的响应开头,但现在您拥有真正的类型安全。
所以--
function call<T>(response:unknown, isCorrectType: (x:unknown)=>x is T):T {
if(isCorrectType(response)) {
return response
} else {
throw("Invalid response")
}
}
注意这里还有一个泛型类型参数,但你不需要显式地把它放进去(这实际上只是一个断言),它是从类型保护的 return 类型推断出来的你通过了。