我有两种类型的React上下文,我需要能够确定它们的类型(移动和桌面)。如何以类型安全的方式这样做?
我尝试编写利用以下属性的用户定义类型防护:React.Context<LayoutContextType>
等效于React.Context<DesktopLayoutContextType> | React.Context<MobileLayoutContextType>
。
但是,在假定它们相等时,我似乎是错误的。
interface ILayoutStateBase {
nightMode: boolean
}
interface ILayoutContextBase<
StateType extends ILayoutStateBase,
Kind extends 'desktop' | 'mobile'
> {
kind: Kind
state: StateType
}
interface IDesktopState extends ILayoutStateBase {
modalOpen: boolean
}
interface IMobileState extends ILayoutStateBase {
sidebarOpen: boolean
}
type DesktopLayoutContextType = ILayoutContextBase<IDesktopState, 'desktop'>
type MobileLayoutContextType = ILayoutContextBase<IMobileState, 'mobile'>
type LayoutContextType =
| DesktopLayoutContextType
| MobileLayoutContextType
// below results in:
/**
* Type 'Context<ILayoutContextBase<IDesktopState, "desktop">>'
* is not assignable to type 'Context<LayoutContextType>'.
*/
const isDesktopLayout = (
ctx: React.Context<LayoutContextType>
): ctx is React.Context<DesktopLayoutContextType> => {
return true // how can I do this?
}
我希望TypeScript能够识别React.Context<LayoutContextType>
等同于React.Context<DesktopLayoutContextType> | React.Context<MobileLayoutContextType>
,并允许我使用displayName属性来区分它们。
但是,我在提供的代码中收到错误消息:
Type 'Context<ILayoutContextBase<IDesktopState, "desktop">>' is not assignable to type 'Context<LayoutContextType>'.
答案 0 :(得分:0)
对不起,因为我的回答可能并不完整,但可能会有所帮助。
为什么下面的代码无法编译?
const isDesktopLayout = (
ctx: React.Context<LayoutContextType>
): ctx is React.Context<DesktopLayoutContextType> => {
return true // how can I do this?
}
让我们看看React.Context<T>
类型。它具有Provider
和Consumer
,它们都使用类型T
并将其用作参数props
的类型。 T
除了作为参数类型之外,没有在其他任何地方使用。
这导致编译错误。要了解原因,请查看下面的简化示例。这是一个接受回调的函数。
function f1 (callback: ((props: string | boolean) => void)) {}
尝试在回调后用f1
调用callback
会出错
f1((props: string) => {}); // Type string | boolean is not assignable to type string
基本上,当您尝试编写类型后卫时,会发生相同的情况。 React.Context<LayoutContextType>
已将Provider
和Consumer
定义为接受类型为props
的{{1}}的函数(经过一些修改,但这并不适用)。并且您无法将上下文LayoutContextType
分配为React.Context<DesktopLayoutContextType>
和Provider
定义为接受类型为Consumer
的{{1}}的函数。
幸运的是,通过使用上下文联合(如下所示)可以轻松解决此问题
props
第二个问题很难解决。类型防护在运行时中用于查找DesktopLayoutContextType
的类型。但是const isDesktopLayout = (
ctx: React.Context<DesktopLayoutContextType> |
React.Context<MobileLayoutContextType>
): ctx is React.Context<DesktopLayoutContextType> => {
return true // how can I do this?
}
和ctx
之间的唯一区别是参数的类型。在运行时,无法找到想要接受的参数回调函数类型。由于JavaScript是动态语言,因此可以使用任何类型的任意数量的参数来调用回调。而且我们无法事先知道参数的类型和数量。
因此,在运行时区分上下文的一种方法可以是React.Context<DesktopLayoutContextType>
属性(如您所建议)。应该在上下文创建期间设置它,之后不要更改它,并签入类型保护。可以这样做
React.Context<MobileLayoutContextType>
但是此解决方案不限于类型,而是绑定到diplayName
的值,该值可以在代码中的任何位置更改,这可能会导致错误。