React Typescript 泛型类型重载(例如 useState)

Posted

技术标签:

【中文标题】React Typescript 泛型类型重载(例如 useState)【英文标题】:React Typescript Generic Type Overloading (e.g. useState) 【发布时间】:2021-05-21 16:04:48 【问题描述】:

我正在尝试创建一个重载函数,该函数将写入和写入会话存储而不是标准状态。我希望它在我只提供类型而不是初始值的情况下是可重载的。就像 useState 在 react 中所做的一样。

这是我目前所拥有的:

export function useSessionState<S = undefined>(initialState?: S): [S, (state: S) => void];
export function useSessionState<S>(initialState: S): [S, (state: S) => void] 
  const [state, setState] = useState<S>()

  // Unimportant: Do something to load to and from session storage.

  return [state, (value) => 

  ]

当我尝试使用这种类型时,我只会得到其中一个重载的结果类型。

// Variable 'state' is a string as expected.
const [state, setState] = useSessionState("some_initial_value")

// Variable 'undefinedState' is also a string, but it should be undefined since no 'initialState' was provided.
const [undefinedState, setUndefinedState] = useSessionState<string>()

【问题讨论】:

【参考方案1】:

我自己也不知道答案,所以我开始阅读和尝试。我的尝试非常接近,但我发现了以下缺陷:

两次尝试都将initialState 显示为S 类型,而不是S | undefined,这意味着您很难在没有强制转换的情况下实现此功能。 第一种方案和第二种方案生成的.d.ts不同。我相信第一次尝试的.d.ts 更接近官方的useState 定义。 即使您没有提及: initialState 可以是一个值,也可以是一个函数,这只是第二次尝试的情况。 setState 函数应该有一个S | undefined 类型的参数,而不是S

希望有更好的解决方案... 如果只有 actual implementation 是用 Typescript 编写的。

尝试 1:Conditional types

Playground

function useState<S>(initialState: S): [S, (state: S) => void];
function useState<S = undefined>(): [S | undefined, (state: S) => void];
function useState<S = undefined>(initialState?: S): [S extends undefined ? undefined | S : S, (state: S) => void] 
  throw new Error(`Not implemented and not using $initialState`);


const [state1, setState1] = useState<string>("initialValue"); // string
const [state2, setState2] = useState("initialValue"); // string
const [state3, setState3] = useState<string>(); // undefined | string
const [state4, setState4] = useState(); // undefined

在操场上生成的.d.ts

declare function useState<S>(initialState: S): [S, (state: S) => void];
declare function useState<S = undefined>(): [S | undefined, (state: S) => void];

尝试 2:没有条件类型

Playground

type Dispatch<T> = (value: T) => void;

function useState<S = undefined>(): [S | undefined, Dispatch<S | undefined>];
function useState<S>(initialState?: S | (() => S)): [S, Dispatch<S>];
function useState<S>(initialState?: S | (() => S)): [S, Dispatch<S>] 
    throw new Error(`Not implemented and not using $initialState`);

 
const [state1, setState1] = useState<string>("initialValue"); // string
const [state2, setState2] = useState("initialValue"); // string
const [state3, setState3] = useState<string>(); // undefined | string
const [state4, setState4] = useState(); // undefined

在操场上生成的.d.ts

declare function useState<S = undefined>(): [S | undefined, Dispatch<S | undefined>];
declare function useState<S>(initialState?: S | (() => S)): [S, Dispatch<S>];

【讨论】:

编辑添加第二次尝试。

以上是关于React Typescript 泛型类型重载(例如 useState)的主要内容,如果未能解决你的问题,请参考以下文章

Typescript核心篇——函数_this指向—重载—泛型

TypeScript——泛型

如何在 Typescript 中正确使用 React.lazy() 来导入带有泛型类型参数的反应组件?

TypeScript泛型

如何告诉 TypeScript 两个泛型类型是相同的?

如何使用泛型缩小 TypeScript 联合类型