Typescript 类型的组合 Ramda lens with React useState set 函数
Typescript types of composition Ramda lens with React useState set function
我正在学习 FP,我正在尝试弄清楚如何在 React 中处理事件。
例如,让我们使用以下场景:
interface Todo {
task: string
done: boolean
}
interface TodoProps {
todo: Todo
onChange: ChangeEventHandler<HTMLInputElement>
}
function TodoItem({todo, onChange}: TodoProps) {
return (
<label>
{todo.task}
<input type="checkbox" checked={todo.done} onChange={onChange}/>
</label>
)
}
function App() {
const [todo, setTodo] = useState({
task: "Some task",
done: false
});
const toggleTodo = // I need to implement this
return (
<main>
<TodoItem onChange={toggleTodo} todo={todo}/>
</main>
)
}
没什么特别的,只是基本的待办事项应用程序。
在缺少的函数中,我需要使用更新的 done
属性 创建对象。为此,我创建了专注于 done
属性.
的 Ramda 镜头
const doneLens = lensProp('done');
那么完成目标应该很容易。我只需要用 Rambda 的 over
.
组合 setTodo
const toggleDone = () => compose(setTodo, over(doneLens, toggleBoolean))(todo)
但问题来了。我收到此 ts 错误:
TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type '<T>(value: T) => T' is not assignable to parameter of type '(x0: unknown, x1: unknown, x2: unknown) => SetStateAction<{ task: string; done: boolean; }>'.
Type 'unknown' is not assignable to type 'SetStateAction<{ task: string; done: boolean; }>'.
Type 'unknown' is not assignable to type '(prevState: { task: string; done: boolean; }) => { task: string; done: boolean; }'.
在纯 js 中这个函数应该可以工作,但是 ts 不能推断 return 类型的 over
函数。这是合乎逻辑的。 over
是通用的,所以让我们尝试添加显式类型。
const toggleDone = () => compose(setTodo, over<Todo>(doneLens, toggleBoolean))(todo)
然后我得到:
TS2554: Expected 3 arguments, but got 2.
index.d.ts(669, 51): An argument for 'value' was not provided.
TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type 'Todo' is not assignable to parameter of type '(x0: unknown, x1: unknown, x2: unknown) => SetStateAction<{ task: string; done: boolean; }>'.
Type 'Todo' provides no match for the signature '(x0: unknown, x1: unknown, x2: unknown): SetStateAction<{ task: string; done: boolean; }>'.
默认情况下 Ramda 函数是柯里化的,但如果我能正确读取错误,似乎当我添加显式类型时柯里化不起作用。
我可以想出解决办法:
const overDone: (t: Todo) => Todo = over(doneLens, toggleBoolean);
const toggleDone = () => compose(setTodo, overDone)(todo);
之所以有效,是因为 overDone
的 return 类型与 setTodo
的输入类型匹配。
但是,我的问题是。 oneliner怎么修?
或者,如果您知道使用镜头、函数组合、useState 挂钩和打字稿处理类似场景的更好方法,我很想知道。
将类型 Todo
添加到 lensProp
调用。您也可以将其移出组件,因为您不需要在组件重新渲染时生成该函数:
const doneLens = lensProp<Todo>("done");
const toggleDone = over(doneLens, not);
因为 setTodo
是一个可以调用另一个函数的函数(参见 https://reactjs.org/docs/hooks-reference.html#functional-updates),它传递当前 todo
,你不需要 R.compose
:
const toggleTodo = () => setTodo(over(doneLens, not));
这就是您的组件的外观 (sandbox):
const doneLens = lensProp<Todo>("done");
const toggleDone = over(doneLens, not);
function App() {
const [todo, setTodo] = useState({
task: "Some task",
done: false
});
const toggleTodo = () => setTodo(toggleDone);
return (
<main>
<TodoItem onChange={toggleTodo} todo={todo} />
</main>
);
}
一个更简单的选择是使用 R.evolve
(sandbox):
const toggleDone = evolve({ done: not });
function App() {
const [todo, setTodo] = useState({
task: "Some task",
done: false
});
const toggleTodo = () => setTodo(toggleDone);
console.log(todo);
return (
<main>
<TodoItem onChange={toggleTodo} todo={todo} />
</main>
);
}
我正在学习 FP,我正在尝试弄清楚如何在 React 中处理事件。 例如,让我们使用以下场景:
interface Todo {
task: string
done: boolean
}
interface TodoProps {
todo: Todo
onChange: ChangeEventHandler<HTMLInputElement>
}
function TodoItem({todo, onChange}: TodoProps) {
return (
<label>
{todo.task}
<input type="checkbox" checked={todo.done} onChange={onChange}/>
</label>
)
}
function App() {
const [todo, setTodo] = useState({
task: "Some task",
done: false
});
const toggleTodo = // I need to implement this
return (
<main>
<TodoItem onChange={toggleTodo} todo={todo}/>
</main>
)
}
没什么特别的,只是基本的待办事项应用程序。
在缺少的函数中,我需要使用更新的 done
属性 创建对象。为此,我创建了专注于 done
属性.
const doneLens = lensProp('done');
那么完成目标应该很容易。我只需要用 Rambda 的 over
.
setTodo
const toggleDone = () => compose(setTodo, over(doneLens, toggleBoolean))(todo)
但问题来了。我收到此 ts 错误:
TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type '<T>(value: T) => T' is not assignable to parameter of type '(x0: unknown, x1: unknown, x2: unknown) => SetStateAction<{ task: string; done: boolean; }>'.
Type 'unknown' is not assignable to type 'SetStateAction<{ task: string; done: boolean; }>'.
Type 'unknown' is not assignable to type '(prevState: { task: string; done: boolean; }) => { task: string; done: boolean; }'.
在纯 js 中这个函数应该可以工作,但是 ts 不能推断 return 类型的 over
函数。这是合乎逻辑的。 over
是通用的,所以让我们尝试添加显式类型。
const toggleDone = () => compose(setTodo, over<Todo>(doneLens, toggleBoolean))(todo)
然后我得到:
TS2554: Expected 3 arguments, but got 2.
index.d.ts(669, 51): An argument for 'value' was not provided.
TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type 'Todo' is not assignable to parameter of type '(x0: unknown, x1: unknown, x2: unknown) => SetStateAction<{ task: string; done: boolean; }>'.
Type 'Todo' provides no match for the signature '(x0: unknown, x1: unknown, x2: unknown): SetStateAction<{ task: string; done: boolean; }>'.
默认情况下 Ramda 函数是柯里化的,但如果我能正确读取错误,似乎当我添加显式类型时柯里化不起作用。
我可以想出解决办法:
const overDone: (t: Todo) => Todo = over(doneLens, toggleBoolean);
const toggleDone = () => compose(setTodo, overDone)(todo);
之所以有效,是因为 overDone
的 return 类型与 setTodo
的输入类型匹配。
但是,我的问题是。 oneliner怎么修? 或者,如果您知道使用镜头、函数组合、useState 挂钩和打字稿处理类似场景的更好方法,我很想知道。
将类型 Todo
添加到 lensProp
调用。您也可以将其移出组件,因为您不需要在组件重新渲染时生成该函数:
const doneLens = lensProp<Todo>("done");
const toggleDone = over(doneLens, not);
因为 setTodo
是一个可以调用另一个函数的函数(参见 https://reactjs.org/docs/hooks-reference.html#functional-updates),它传递当前 todo
,你不需要 R.compose
:
const toggleTodo = () => setTodo(over(doneLens, not));
这就是您的组件的外观 (sandbox):
const doneLens = lensProp<Todo>("done");
const toggleDone = over(doneLens, not);
function App() {
const [todo, setTodo] = useState({
task: "Some task",
done: false
});
const toggleTodo = () => setTodo(toggleDone);
return (
<main>
<TodoItem onChange={toggleTodo} todo={todo} />
</main>
);
}
一个更简单的选择是使用 R.evolve
(sandbox):
const toggleDone = evolve({ done: not });
function App() {
const [todo, setTodo] = useState({
task: "Some task",
done: false
});
const toggleTodo = () => setTodo(toggleDone);
console.log(todo);
return (
<main>
<TodoItem onChange={toggleTodo} todo={todo} />
</main>
);
}