Типы для обновления функции записи с глубоким путем и обратным вызовом - PullRequest
1 голос
/ 25 октября 2019

Я пытаюсь создать функцию обновления, которая обновляет глубокие значения в записи. У меня есть перегруженные варианты для путей различной глубины.

Я не могу понять, как правильно ввести функцию обратного вызова, используемую для обновляемого значения.

interface Test {
    foo?: { bar: number }
}
const input: Test = { foo: { bar: 1 } }

update(input, 'foo', 'bar')(v => v + 1)

Когда яиспользуйте функцию, она говорит мне, что «Object (v) имеет тип unknown».

Но, например, у меня есть похожая функция set, она определена почти так же, но она правильно печатается при использовании следующим образом:

set(input, 'foo', 'bar')(2)

Вот моя функция

type UpdateFn<T> = (value: T) => T
export function update<T extends Record<string, any>, K1 extends keyof T>(
    record: T | undefined,
    key1: K1
): (callback: UpdateFn<NonNullable<T[K1]>>) => T
export function update<
    T extends Record<string, any>,
    K1 extends keyof T,
    K2 extends keyof NonNullable<T[K1]>
>(
    record: T | undefined,
    key1: K1,
    key2: K2
): (callback: UpdateFn<NonNullable<T[K1][K2]>>) => T

export function update<
    T extends Record<string, any>,
    K1 extends keyof T,
    K2 extends keyof NonNullable<T[K1]>
>(
    record: T | undefined,
    key1: K1,
    key2?: K2
): (
    callback:
        | UpdateFn<NonNullable<T[K1]>>
        | UpdateFn<NonNullable<T[K1][K2]>>
) => T | undefined {
    return callback => {
        if (record === undefined) return record

        if (key2 === undefined) {
            const value = get(record, key1)
            if (value === undefined) return record
            return set(record, key1)(callback(value))
        } else {
            const value = get(record, key1, key2)
            if (value === undefined) return record
            return set(record, key1, key2)(callback(value))
        }
    }
}

Установить (работает правильно):

export function set<
    T extends Record<string, any>,
    K1 extends keyof T,
    K2 extends keyof NonNullable<T[K1]>
>(record: T | undefined, key1: K1, key2: K2): (value: T[K1][K2]) => T

1 Ответ

1 голос
/ 25 октября 2019

Предполагая, что я просто пытаюсь обратиться к типам, а не к реализации, ваша вторая перегрузка, вероятно, должна выглядеть примерно так:

export function update<
    T extends Record<string, any>,
    K1 extends keyof T,
    K2 extends keyof NonNullable<T[K1]>
>(
    record: T | undefined,
    key1: K1,
    key2: K2
): (callback: UpdateFn<NonNullable<NonNullable<T[K1]>[K2]>>) => T

Это дополнительное значение NonNullable гарантирует, что выречь идет о типе record[key1][key2], если record и record[key1] оба определены / не равны нулю. Могут быть другие, более общие или более чистые способы ввода update(), но это, по крайней мере, устраняет проблему, с которой вы столкнулись:

update(input, 'foo', 'bar')(v => v + 1); // okay

Надеюсь, это поможет;удачи!

Ссылка на код

...