如何构建一个typescript函数,该函数从另一个对象返回具有给定键的记录

v8wbuo2f  于 2023-01-14  发布在  TypeScript
关注(0)|答案(2)|浏览(118)

我需要构建一个函数,从给定对象中获取0个或多个键,并返回只包含这些给定键的记录。
但是,我知道如何从对象请求一个键数组参数:

function myFunc<T>(field: (keyof T)[]): void {}

问题是,我不知道如何使它返回一个以给定字段为键的记录,而不是返回void:

function myFunc<T, K extends (keyof T)[]>(fields: K): Record<K, number> {}

这将产生Type 'K' does not satisfy the constraint 'string | number | symbol'.,这是合理的。
为了更清楚起见,下面是这个函数在javascript中的实现:

function myFunc(fields) {
   return fields.reduce((result, key, idx) => ({...result, [key]: idx}), {});
}

下面是该函数的输入和预期输出的示例:

type Person = {
  name: string,
  age: number,
  citizen: boolean
}

console.log(myFunc<Person>(['name', 'citizen'])); // Object { name: 0, citizen: 1 }
console.log(myFunc<Person>(['name', 'years'])); // Error: Argument of type '"years"' is not assignable to parameter of type '"name" | "age" | "citizen"'

看起来这应该是一个很琐碎的事情,我完全错过了,但我不能找到解决办法,即使在互联网上搜索😔
先谢了!

ne5o7dgx

ne5o7dgx1#

请看这个例子:

type Person = {
    name: string,
    age: number,
    citizen: boolean
}

type Result<Fields extends PropertyKey[]> = {
    [Prop in keyof Fields as Fields[Prop] extends Fields[number] ? Fields[Prop] : never]: Exclude<Prop, number>
}

function withObj<Obj,>(obj: Obj): <Field extends keyof Obj, Fields extends Field[]>(fields: [...Fields]) => Result<Fields>
function withObj<Obj,>(obj: Obj) {
    return (fields: string[]) =>
        fields.reduce((result, key, idx) => ({ ...result, [key]: idx }), {});
}

const myFunc = withObj({
    name: 'John',
    age: 42,
    citizen: true
})

const x = myFunc(['name', 'citizen']) // ok
x.name // 0
const y = myFunc(['name', 'years']) // error

Playground
Result遍历每个数组属性,并检查Fields[index]是否为数组元素的子类型。如果是,则将数组键重命名为适当的数组元素,并在值的位置使用index。
此外,我还添加了myFunc的咖喱版本,只是为了进行适当的推理。

c0vxltue

c0vxltue2#

如果我没理解错的话,您希望函数返回一个包含参数键和整数值的对象,但前提是数组中的值是T的键。
这可能就足够了:

function myFunc<T>(fields: (keyof T)[]): Partial<Record<keyof T, number>> {
  return fields.reduce((result, key, idx) => ({ ...result, [key]: idx }), {});
}

更准确的说法是:

function myFunc<T>(fields: (keyof T)[]): Record<typeof fields[number], number> {
  return fields.reduce(
    (result, key, idx) => ({ ...result, [key]: idx }),
    {}
  ) as Record<typeof fields[number], number>;
}

因为它使用参数数组中的键值。但是,我们需要使用as,因为{}混淆了类型脚本,因为{}是Partial类型,并且类型脚本无法确定它在reduce结束时是否变为完整的ReturnType。
另一种可能性,但非常冗长。注意as constreadonly的用法:

function myFunc<H, T extends keyof H>(
  fields: readonly T[]
): Record<typeof fields[number], number> {
  return fields.reduce(
    (result, key, idx) => ({ ...result, [key]: idx }),
    {}
  ) as Record<typeof fields[number], number>;
}

type Person = {
  name: string;
  age: number;
  citizen: boolean;
};

// const array = ["name", "citizen", "year"] as const // -> Type '"name" | "citizen" | "year"' does not satisfy the constraint 'keyof Person'.
const array = ["name", "citizen"] as const;
const result = myFunc<Person, typeof array[number]>(array);

相关问题