具有 propertyname 和 value 参数的通用函数

Generic function with propertyname and value argument

是否可以编写一个带有 2 个参数的通用函数,其中第一个参数是属性名称,第二个是属性值?

这个(不工作的)打字稿显示了我的意图:

declare function add<F extends Record<string, unknown>>(name: keyof F, value: T[path]): void;
add<{a: number; b: boolean}>('a', true); // Should be number

因此,当我使用错误的值参数调用 add 时,出现类型错误。

编辑 我意识到我真正想要的是键入带有重载签名的函数。

declare function add<F extends any>(obj: F, prop: keyof F, value: F[typeof prop]): void;
add({a: 1, b: false}, 'b', 42); // should give compiler error

这是值参数,编译器将其推断为 number | boolean,因此两者都满足。但是显然,没有办法根据第一个参数来反映类型。

你可以柯里化你的函数:

const add = <
    Key extends PropertyKey,
    Value,
    Obj extends Record<Key, Value>,
    Name extends keyof Obj
>(obj: Obj, name: Name, value: Obj[Name]):
    Obj[Name] => null as any

const result = add({ a: 42, b: true }, 'a', 42)

add({ a: 1, b: false }, 'b', 42); // error


函数的第一部分:

Key - 推断对象键

Value - 推断对象值

Obj - 推断整个对象

Name - 推断 name 参数

Obj[Name] - 我不确定 value 限制。

Playground

Here,在我的博客中,您可以找到有关函数参数推断的更多信息

更新

我认为你需要推断 obj

这里有一个替代版本:

type Obj = { a: number; b: boolean }

const add = <
    Name extends keyof Obj
>(name: Name, value: Obj[Name]):
    Obj[Name] => null as any

const result = add('a', 42)

add('b', 42); // error

Playground

我已经针对我自己的问题提出了一个通用的解决方案,它不需要运行时函数来解决问题。 它涉及创建两个实用程序类型:UnionToIntersection 和 FuncOverloads。 从后面看,FuncOverloads 生成一个联合类型,其中包含从类型参数反映的所有方法签名:

type FuncOverloads<T> = T extends Record<string, unknown>
? { [P in keyof T]: ((path: P, value: T[P]) => void) }[Exclude<keyof T, symbol>]
: never;

因此它生成了一个如下所示的类型:

Type AddOverloads = FuncOverloads<{a: number; b: boolean}>; 
//   ((path: "a", value: number) => void) | ((path: "b", value: boolean) => void)

这个函数联合类型,然后得到具体的,当通过UnionToIntersection utility-type:

type UnionToIntersection<U> = 
      (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

所以我最终得到了一个重载的函数签名:

declare const add: UnionToIntersection<FuncOverloads<{a: number; b: boolean}>>;
add('a', true); // Gives compiler error.
add('a', 42); // All fine

因此 playground 表明,我关于无法处理泛型重载的假设是错误的:)