[英]In TypeScript, how can I infer my arguments and impose a constraint that all items be keyof T, where T is the generic type for the function?
My goal here is to create a function named: getFields
.我的目标是创建一个名为:
getFields
的函数。 This function has a generic <T>
and a parameter ...fields: Array<keyof T>
.这个函数有一个通用的
<T>
和一个参数...fields: Array<keyof T>
。 I would like this function to return a function which when given an object of type <T>
will return a reduced object with only the properties named in ...fields
.我希望这个函数返回一个函数,当给定一个
<T>
类型的对象时,该函数将返回一个简化的对象,其中只包含...fields
命名的属性。
The following is one helper type and my getFields
implementation:以下是一种辅助类型和我的
getFields
实现:
type SubObj<T, S extends Array<keyof T>> = Pick<
T,
keyof { [K in S[number]]: K extends keyof T ? K : never }
>;
export function getFields<T extends Record<string, unknown>>(
...fields: Array<keyof T>
): (obj: T) => SubObj<T, typeof fields> {
return (obj: T) =>
Object.fromEntries(fields.map((field) => [field, obj[field]])) as SubObj<
T,
typeof fields
>;
}
I tested this implementation with the following code:我使用以下代码测试了这个实现:
type A = {
a: string;
b: string;
c: string;
};
const b = getFields<A>('a', 'c')({ a: '', b: '', c: '' });
However, when I look at the typeof
b
it is Pick<A, "a" | "b" | "c">
但是,当我查看
typeof
b
它是Pick<A, "a" | "b" | "c">
Pick<A, "a" | "b" | "c">
Pick<A, "a" | "b" | "c">
. Pick<A, "a" | "b" | "c">
。 What I really want is Pick<A, "a" | "c">
我真正想要的是
Pick<A, "a" | "c">
Pick<A, "a" | "c">
. Pick<A, "a" | "c">
。
I have tried a lot of things to make this work the way I intend it to, but the only success was adding a second generic argument which would require me to change the code to this:我已经尝试了很多方法来使这项工作按照我想要的方式工作,但唯一的成功是添加了第二个通用参数,这需要我将代码更改为:
const b = getFields<A, ['a','c']>('a', 'c')({ a: '', b: '', c: '' });
This is too redundant for me to see as acceptable.这对我来说太多余了,无法接受。
At this point, I think I've hit the limits of my TypeScript abilities because I can't think of any other way to accomplish what I'm looking for.在这一点上,我认为我已经达到了我的 TypeScript 能力的极限,因为我想不出任何其他方法来完成我正在寻找的东西。
Is this even possible to do with TypeScript?这甚至可以用 TypeScript 做吗? If so, what do I need to do?
如果是这样,我需要做什么?
TypeScript does not currently support partial type parameter inference (see microsoft/TypeScript#26242 ). TypeScript 目前不支持部分类型参数推断(请参阅microsoft/TypeScript#26242 )。 If you have multiple type parameters in a type/function, you either need to specify them all explicitly, or let them all be inferred.
如果您在一个类型/函数中有多个类型参数,您要么需要明确指定它们,要么让它们全部被推断出来。 There is no way to specify one and let the other be inferred.
没有办法指定一个并让另一个被推断出来。 If you want to go that route, there are workarounds, but then this question becomes a duplicate of other such questions, and I can point to, for example, this answer for how to proceed.
如果你想走那条路,有一些解决方法,但是这个问题会成为其他此类问题的重复,例如,我可以指出如何继续的答案。
Stepping back, though, I'd say that the example code here might possibly be expressed in a simpler way that doesn't require anyone specify any types at all.不过,退一步说,这里的示例代码可能会以更简单的方式表达,根本不需要任何人指定任何类型。 Consider this:
考虑一下:
export function getFields<K extends PropertyKey>(
...fields: K[]
) {
return <T extends Record<K, unknown>>(obj: T) =>
Object.fromEntries(fields.map((field) => [field, obj[field]])) as Pick<T, K>;
}
Here we are completely removing the object type T
from the call signature of getFields()
.在这里,我们从
getFields()
的调用签名中完全删除了对象类型T
All getFields()
cares about is getting a list of key-like arguments.所有
getFields()
关心的是获取一个类似键的参数列表。 It then returns a function which is also generic , and this one cares about the object type T
, and constrains it to be something with the keys from K
.然后,它返回一个函数,它也是通用的,关于对象类型此人关心
T
,并约束它被什么东西从按键K
。 Both T
and K
are therefore inferrable:因此
T
和K
都是可推断的:
const b = getFields('a', 'c')({ a: '', b: '', c: '' });
/* const b: Pick<{
a: string;
b: string;
c: string;
}, "a" | "c"> */
Since the return value of getFields()
is a generic function, it can be used for types other than A
, for example:由于
getFields()
的返回值是一个泛型函数,因此它可以用于A
以外A
类型,例如:
const c = getFields('a', 'c')({ a: 1, b: 2, c: 3 }) // {a: number, c: number}
But should still error if you give it something inappropriate:但是如果你给它一些不合适的东西,它仍然会出错:
const d = getFields('a', 'c')({ a: 1 }) // error! property 'c' is missing
If you really care about specifying T
you can do it when calling the return function:如果你真的关心指定
T
你可以在调用返回函数时这样做:
const e = getFields('a', 'c')<A>({ a: 1, b: 2, c: 3 }); // error! number is not string
If for some reason you really want to specify T
at the beginning and infer K
, you need to use one of the workarounds for partial type inference, like even more currying :如果由于某种原因你真的想在开头指定
T
并推断K
,你需要使用部分类型推断的解决方法之一,比如更多的柯里化:
const stricterGetFields = <T,>() => <K extends keyof T>(...fields: K[]) => (obj: T) =>
Object.fromEntries(fields.map((field) => [field, obj[field]])) as Pick<T, K>;
const f = stricterGetFields<A>(); // specify here and do nothing else
const g = f('a', 'c')({ a: "", b: "", c: "" }); // Pick<A, 'a'|'c'>;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.