简体   繁体   中英

How to build a typescript function that returns an record with the given keys from another object

I need to build a function that takes 0 or more keys from a given object and returns a record with those given keys only.

I know, however, how to request a parameter that is an array of keys from an object:

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

Problem is that I'm not sure how to make it so that instead of returning void, it returns a record with the given fields as keys:

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

This yields Type 'K' does not satisfy the constraint 'string | number | symbol'. Type 'K' does not satisfy the constraint 'string | number | symbol'. which is fair.

To make it clearer, this would be the implementation of this function in javascript:

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

And this would be an example of the input and expected output for that function:

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"'

It looks like it should be a very trivial thing that I'm missing completely but I can't find the solution even by searching on inte.net

Thanks in advance!

Consider this example:

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 iterates through each array prop and check whether Fields[index] is a subtype of array elements. If yes - rename array key to appropriate array element and use index in a place of a value.

Also, I have added curried version of myFunc just for the proper inference.

If I understand you correctly, you want your function to return an object with keys from argument and integral values, but only if values in array are keys of T.

This may be sufficient:

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

More accurate would be:

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>;
}

Since it uses for keys values in argument array. However, we need to use as , since {} confuses typescript, because {} is of type Partial and typescript cannot determine that it becomes full ReturnType at end of reduce.

Another possibility, but very verbose. Note use of as const and readonly :

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM