具有深度路径和回调的更新记录函数的类型
Types for update Record function with deep path and callback
我正在尝试创建一个更新函数来更新记录中的深度值。我为不同深度的路径重载了变体。
我似乎无法弄清楚如何正确键入用于要更新的值的回调函数。
interface Test {
foo?: { bar: number }
}
const input: Test = { foo: { bar: 1 } }
update(input, 'foo', 'bar')(v => v + 1)
当我使用函数时,它告诉我 "Object(v) is of type 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
假设我只是想解决类型而不是实现问题,您的第二个重载可能应该是这样的:
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
如果 record
和 record[key1]
都是 defined/non-null,那里面的额外 NonNullable
确保你在谈论 record[key1][key2]
的类型。可能还有其他更通用或更简洁的方法来为 update()
进行输入,但这至少解决了您看到的问题:
update(input, 'foo', 'bar')(v => v + 1); // okay
希望对您有所帮助;祝你好运!
我正在尝试创建一个更新函数来更新记录中的深度值。我为不同深度的路径重载了变体。
我似乎无法弄清楚如何正确键入用于要更新的值的回调函数。
interface Test {
foo?: { bar: number }
}
const input: Test = { foo: { bar: 1 } }
update(input, 'foo', 'bar')(v => v + 1)
当我使用函数时,它告诉我 "Object(v) is of type 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
假设我只是想解决类型而不是实现问题,您的第二个重载可能应该是这样的:
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
如果 record
和 record[key1]
都是 defined/non-null,那里面的额外 NonNullable
确保你在谈论 record[key1][key2]
的类型。可能还有其他更通用或更简洁的方法来为 update()
进行输入,但这至少解决了您看到的问题:
update(input, 'foo', 'bar')(v => v + 1); // okay
希望对您有所帮助;祝你好运!