【问题标题】:Types for update Record function with deep path and callback具有深度路径和回调的更新记录函数的类型
【发布时间】:2020-02-21 18:39:41
【问题描述】:

我正在尝试创建一个更新函数来更新记录中的深层值。我为不同深度的路径重载了变体。

我似乎无法弄清楚如何正确键入用于要更新的​​值的回调函数。

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

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

当我使用该函数时,它告诉我“Object(v) 的类型未知”。

但例如我有类似的 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

【问题讨论】:

  • 你真的需要深度吗?例如你不能把update(input, 'foo', 'bar')(v =&gt; v + 1) 减少到update(input.foo, 'bar')(v =&gt; v + 1) 吗?
  • 是的,我需要深度,因为函数需要返回整个更新的输入对象,只是 input.foo。否则它不容易组合。

标签: typescript typescript-generics


【解决方案1】:

假设我只是想解决类型而不是实现,您的第二个重载可能应该是这样的:

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

如果recordrecord[key1] 都已定义/非空,则其中的额外NonNullable 确保您正在谈论record[key1][key2] 的类型。可能还有其他更通用或更简洁的方法来为update() 进行打字,但这至少解决了您看到的问题:

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

希望有所帮助;祝你好运!

Link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 2020-05-18
    • 1970-01-01
    • 2018-12-23
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    相关资源
    最近更新 更多