Функция increment
возвращает определенный набор клавиш State
. Функция Updater
должна возвращать подмножество ключей State
, основываясь не на собственной логике, а на параметре типа, переданного ей.
Если нам все равно, какое подмножество State
будет возвращено, мы могли бы использовать Partial<T>
, но это сделало бы функцию бесполезной для setState
, что, как я догадываюсь, имеет смысл:
type State = Readonly<typeof initialState>
type Updater<S> = (prevState: S) => Partial<S> | null
const increment: Updater<State> = (prevState: State) => ({
count: prevState.count + 1,
})
class Foo extends React.Component<{}, typeof initialState>
{
foo(){
this.setState(increment(this.state)); // Would be an error because count is number | undefined
}
}
Другой вариант - зафиксировать в Updater
клавиши, которые должна возвращать функция, путем перемещения параметра типа в Updater
из функции:
type Updater<S, K extends keyof S> = (prevState: S) => Pick<S, K> | null
const increment: Updater<State, 'count'> = (prevState: State) => ({
count: prevState.count + 1,
})
Это позволяет нам использовать функцию в setState
, но нам нужно явно указать параметр типа. Лучший вариант - использовать заводскую функцию, которая для нас выведет K
.
type Updater<S, K extends keyof S> = (prevState: S) => Pick<S, K> | null
function createUpdater<S>() {
return function<K extends keyof S> (fn: (state: S) => Pick<S, K>) : Updater<S, K> {
return fn;
}
}
const increment = createUpdater<State>()((prevState: State) => ({
count: prevState.count + 1,
}));
Хотя лучшим решением может быть не указывать тип increment
, а позволить компилятору работать со всеми типами.