简体   繁体   中英

How assign a value type to a generic key type of an object in typescript?

I'm trying to write a function that merges, and sorts already sorted arrays into one sorted array. It accepts array of array of objects, and a key to compare which will be the base of the comparison. I need to make sure that the value of the provided key is always a number.

Here's what I'm at so far:

const sortMerge = <
  A extends Array<I>,
  K extends keyof A[number],
  I = A[number] & {
    [key in K]: number;
  },
>(
  arrays: A[],
  key: K,
  sortMethod = SortMethod.asc,
) => {
  const indexesOfArrays = arrays.map(() => 0);

  const mergedSorted = [];

  while (arrays.some((array, i) => array.length > indexesOfArrays[i])) {
    const currentItemsOfArrays = arrays.map(
      (array, arrayIndex) => array[indexesOfArrays[arrayIndex]],
    );
    const comparison = currentItemsOfArrays.map((item) =>
      item ? item[key] : (sortMethod === SortMethod.asc ? Infinity : -Infinity),
    );

    const nextArrayIndex = comparison.indexOf(
      Math[sortMethod === SortMethod.asc ? 'min' : 'max'](...comparison),
    );
    const nextItem = currentItemsOfArrays[nextArrayIndex];

    mergedSorted.push(nextItem);
    indexesOfArrays[nextArrayIndex]++;
  }
  return mergedSorted;
};

Everything's fine, but it doesn't recognise I[K] as number , but that's what I was trying to do with defining both K , and I as generics.

What am I doing wrong?

Expected errors/types:

 const missingKey = [ { a: 1 } ]; const valid = [ { a: 2, b: 3 } ]; const anotherValid = [ { c: 3, b: 4 } ]; sortMerge([missingKey, valid], 'b') // missingKey[number] is missing property 'b'; sortMerge([valid, anotherValid], 'b') // expected return type: ({ a: number, b: number } | { c: number, b: number })[]

I have changed generic constraints a bit:

enum SortMethod {
  asc = 'asc',
}
const sortMerge = <
  Key extends PropertyKey,
  Elem1 extends Record<Key, number>,
  Elem2 extends Record<Key, number>,

  Arr1 extends Elem1[],
  Arr2 extends Elem2[],
  Arrays extends [Arr1, Arr2]
>(
  arrays: [...Arrays],
  key: Key,
  sortMethod = SortMethod.asc,
): (Arrays[number][number])[] => {
  const indexesOfArrays = arrays.map(() => 0);

  const mergedSorted = [];

  while (arrays.some((array, i) => array.length > indexesOfArrays[i])) {
    const currentItemsOfArrays = arrays.map(
      (array, arrayIndex) => array[indexesOfArrays[arrayIndex]],
    );
    const comparison = currentItemsOfArrays.map((item) =>
      item ? item[key] : (sortMethod === SortMethod.asc ? Infinity : -Infinity),
    );

    const nextArrayIndex = comparison.indexOf(
      Math[sortMethod === SortMethod.asc ? 'min' : 'max'](...comparison),
    );
    const nextItem = currentItemsOfArrays[nextArrayIndex];

    mergedSorted.push(nextItem);
    indexesOfArrays[nextArrayIndex]++;
  }
  return mergedSorted;
}

const missingKey = [{ a: 1 }];
const valid = [{ a: 2, b: 3 }];
const anotherValid = [{ c: 3, b: 4 }];

sortMerge([missingKey, valid], 'b') // missingKey[number] is missing property 'b';
const x = sortMerge([valid, anotherValid], 'b')

Playground

If you want to infer keys of nested object or object in the array, you should start from the bottom.

Key - infered property of object

Elem - infered object where keys are Key

Arr - infered array of Elem

Arrays - infered array of Arr

As you might have noticed, I have started from infering the bottom level of prop and finished infering the top argument.

If you are interested in type inference on function arguments, you can check my article

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