我有两个具有相同功能的版本;对于第一个(下面的propEq
),typescript可以推断出key
参数可以并且应该是类型为T
的对象的键。
对于第二个功能(下面的propEq2
),key
的类型为never
。如果我显式提供类型参数,它将毫无问题地工作。我猜这是因为propEq2
的第一部分仅知道调用后T
的类型,我知道为什么会有问题。但是即使我在对象上调用它,我仍然会遇到相同的错误。
我可以通过任何方式正确键入这种功能吗?
export const propEq = <T, K extends keyof T>(key: K, equals: T[K], object: T) => object[key] === equals;
export const propEq2 = <T, K extends keyof T>(key: K, equals: T[K]) => (object: T) => object[key] === equals;
答案 0 :(得分:2)
因此,您面临的问题是-通过设计TS的工作方式。类型推断是从左到右,从上到下进行的。同样,这里的常识表明,无法实现您的要求,就像您说K extends keyof T
之前K
是never
一样,直到知道T
为止,就像设置键时没有对象一样却在类型级别提供了确切的信息,
type K = keyof unknown; // K is never
这就是为什么如果您首先提供K
,则需要在T
之前显式提供K
,那么keyof T
可以被评估为有意义的类型。
您也可以通过还原的方式进行操作,因此首先声明键和值,然后再将要求设置为对象,以其他方式进行。考虑以下:
const propEq = <K extends PropertyKey, V>(key: K, equals: V) =>
<T extends Record<K, V>>(object: T) => object[key] === equals;
propEq3('a', 1)({a: 2}) // yes correct
propEq3('a', 1)({a: 'str'}) // error as it should, `a` has different type
propEq3('a', 1)({b: 1}) // error as it should, there is no `a` in object
在这里做什么:
<K extends PropertyKey, V>
我们说K
是某种属性类型,而V
是任何属性K
和V
两个参数都从key
和equals
推论得出object
被限制为T extends Record<K, V>
,因此我们只允许比较具有此键和此类型值的对象上述解决方案非常好,因为我们不将方程式限制为单一类型,但是我们新创建的propEq
可以与任何具有想要的键和值类型的对象一起使用。