使用 React 和 Typescript 描述柯里化状态处理程序的类型
Describe type of curried state handler with React and Typescript
我正在尝试描述处理更改事件以反映状态值的函数类型。
所需的实现如下所示:
handleChange: ChangeHandler<State> = field => value =>
this.setState({ [field]: value });
给定状态:
interface State {
title: string;
description: string | null;
}
可以将此函数传递给组件:
<TextComponent onChange={this.handleChange('title')} />
我解决这个问题的最佳尝试是创建以下类型:
type ChangeHandler<S> = <K extends keyof S, V extends S[K]>(key: K) => (value: V) => void;
但是,这似乎只有在状态的所有属性都是可选的情况下才有效。我无法设法让它与可选和非可选属性的状态一起使用。
编译器错误:
[ts] Argument of type '{ [x: string]: V; }' is not assignable to
parameter of type 'State | ((prevState: Readonly, props:
Readonly) => State |
Pick | null) | Pick<...> | null'.
Type '{ [x: string]: V; }' is missing the following properties from
type 'Pick': title, description [2345]
您不能定义 V extends S[K]
,因为您可以直接内联此声明,如下所示:
type ChangeHandler<S> = <K extends keyof S>(key: K) => (value: S[K]) => void;
根本原因
这是由 TypeScript 的 known issue 引起的,其中计算的 属性 名称由 文字联合 组成。
在handleChange
中,field
的类型从'title' | 'description'
扩大到string
,导致this.setState
由于[=的方式而失败17=] 的类型已定义。
下面是setState
的简化定义:
function setState<K extends keyof State>(s: Pick<State, K>): void {
console.log(s);
}
将该定义应用于handleChange
时,上面的K
变为'title' | 'description'
,s
参数变为Pick<State, 'title' | 'description'>
,本质上等同于State
界面。
因此,鉴于我们的 field
已扩大到 string
并且 this.setState
在这种情况下接受 State
,我们将得到您所看到的错误因为我们使用以下类型调用 this.setState
:
{ [x: string]: V }
无法分配给 State
,因为它缺少必需的参数 title
和 description
。
解决方案
我们最好的选择是使用 类型断言 来消除错误,同时保持类型检查。更新后的定义如下:
let handleChange: ChangeHandler<State> = field => value =>
setState({ [field]: value } as unknown as State);
let x = handleChange('title');
x('test'); // OK
x(50); // Error: should be string
参见包含上述代码的 this playground link。
请参阅 discussion 上有关 DefinitelyTyped 的此特定问题的替代解决方案/方法。
我正在尝试描述处理更改事件以反映状态值的函数类型。
所需的实现如下所示:
handleChange: ChangeHandler<State> = field => value =>
this.setState({ [field]: value });
给定状态:
interface State {
title: string;
description: string | null;
}
可以将此函数传递给组件:
<TextComponent onChange={this.handleChange('title')} />
我解决这个问题的最佳尝试是创建以下类型:
type ChangeHandler<S> = <K extends keyof S, V extends S[K]>(key: K) => (value: V) => void;
但是,这似乎只有在状态的所有属性都是可选的情况下才有效。我无法设法让它与可选和非可选属性的状态一起使用。
编译器错误:
[ts] Argument of type '{ [x: string]: V; }' is not assignable to parameter of type 'State | ((prevState: Readonly, props: Readonly) => State | Pick | null) | Pick<...> | null'.
Type '{ [x: string]: V; }' is missing the following properties from type 'Pick': title, description [2345]
您不能定义 V extends S[K]
,因为您可以直接内联此声明,如下所示:
type ChangeHandler<S> = <K extends keyof S>(key: K) => (value: S[K]) => void;
根本原因
这是由 TypeScript 的 known issue 引起的,其中计算的 属性 名称由 文字联合 组成。
在handleChange
中,field
的类型从'title' | 'description'
扩大到string
,导致this.setState
由于[=的方式而失败17=] 的类型已定义。
下面是setState
的简化定义:
function setState<K extends keyof State>(s: Pick<State, K>): void {
console.log(s);
}
将该定义应用于handleChange
时,上面的K
变为'title' | 'description'
,s
参数变为Pick<State, 'title' | 'description'>
,本质上等同于State
界面。
因此,鉴于我们的 field
已扩大到 string
并且 this.setState
在这种情况下接受 State
,我们将得到您所看到的错误因为我们使用以下类型调用 this.setState
:
{ [x: string]: V }
无法分配给 State
,因为它缺少必需的参数 title
和 description
。
解决方案
我们最好的选择是使用 类型断言 来消除错误,同时保持类型检查。更新后的定义如下:
let handleChange: ChangeHandler<State> = field => value =>
setState({ [field]: value } as unknown as State);
let x = handleChange('title');
x('test'); // OK
x(50); // Error: should be string
参见包含上述代码的 this playground link。
请参阅 discussion 上有关 DefinitelyTyped 的此特定问题的替代解决方案/方法。