给出此类型:
type Obj = $ReadOnly<{|
foo: string,
bar: number
|}>;
具有应用部分更新的更新程序功能:
const update = (u: $Shape<Obj>) => (obj: Obj) => ({...obj, ...u});
如何编写一个接受键和值的函数,并以类型安全的方式将其应用于Obj
?我尝试过:
const setKeyValue = (key: $Keys<Obj>) =>
(value: $ElementType<Obj, typeof key>) =>
update({[key]: value});
但是Flow complains无法将string
写入bar
,也不能将number
写入foo
。 IOW,$ElementType<>
定义似乎无法正确找到与传递的key
相对应的类型。
答案 0 :(得分:2)
$ElementType<Obj, typeof key>
的结果为number & string
。这是因为Keys<Obj>
返回所有可能键值的并集,而ElementType则返回所有可能值的交集。
type Obj = $ReadOnly<{| foo: string, bar: number |}>;
{
let k: 'foo' = 'foo';
// Works because Flow knows `k` is exactly `foo` and its value can only be a string.
let v: $ElementType<Obj, typeof k> = 'wow';
};
{
let k: $Keys<Obj> = 'foo';
// ERROR: Flow can't know `typeof k` is exactly `foo` and not also `bar`.
let v: $ElementType<Obj, typeof k> = 'wow';
};
number & string
不太有用:
// Cannot assign `'wow'` to `a` because string [1] is incompatible with number [2].
let a: number & string = 'wow';
// Cannot assign `10` to `b` because number [1] is incompatible with string [2].
let b: number & string = 10;
获得所需行为的一种可能方法是使用泛型。也就是说,看起来像ElementType doesn't yet work as expected with Generics
在解决上述问题之前,我认为使用any
是合理的情况。它是相对隔离的,不会泄漏到您的功能之外。
{
const setKeyValue = (key: $Keys<Obj>) =>
(value: $ElementType<Obj, typeof key>) =>
update(({[key]: value}: any));
};
就像票证中提到的那样,有多种方法可以使Flow理解这种类型安全的方法,但是它们更加冗长。
{
const setKeyValue = (key: $Keys<Obj>) => {
return {
foo: (value) => update(({[key]: value})),
bar: (value) => update(({[key]: value})),
}[key]; // using an object here is helpful in case you forget a field or if one is added later, Flow will error.
};
};
当Flow不确定某件事时,通常会出错。如果不确定,TypeScript通常将保持沉默。
即,TypeScript中不会出现以下错误,但应该会出现。
interface Obj { foo: string; bar: number; }
const update = (u: Partial<Obj>) => (obj: Obj): Obj => ({...obj, ...u});
const setKeyValue = <TKey extends keyof Obj>(key: TKey) =>
(value: Obj[TKey]) => update({[key]: {'crazy': 'crazy stuff that should error'}})
const w: Obj = {foo: 'hello', bar: 5};
setKeyValue('foo')('world')(w);