我试图描述它处理更改事件,以反映状态值的函数的类型。
所需的实现将如下所示:
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]类型{的”参数[X:字符串]:V; }“不是分配给参数类型的”国| ((prevState:只读,道具:只读)=>国家|接| NULL)|匹克<...> |空值'。 类型“{[X:字符串]:V; }”缺少从类型以下属性‘挑选’:标题,描述[2345]
这是通过打字稿,其中包括文字的工会计算属性名被加宽一个known issue引起的。
在handleChange
,field
的类型是从'title' | 'description'
加宽到string
,这会导致this.setState
失败由于this.setState
的方式分型中定义。
下面是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这一具体问题。
就不能定义为V extends S[K]
你可以直接内联这个声明如下:
type ChangeHandler<S> = <K extends keyof S>(key: K) => (value: S[K]) => void;