Obtuve una función simple con una variedad de objetos. En esta matriz siempre habrá al menos una propiedad string .
En el cuerpo, quiero poder seleccionar dinámicamente esa string por key y ordenar con localCompare .
El problema es que las propiedades pueden tener numbers y al hacer localCompare TS se queja.
;(() => { type entries = { [key: string]: string | number } const data: entries[] = [ { key: 'bbb', number: 1 }, { key: 'aaa', number: 2} ] // Let's imagine index comes as a parameter const index = 'key' const order: entries[] = data.sort((a, b) => a[index].localeCompare(b[index])) console.log(order) })()Solo asegúrese de convertir siempre las propiedades en cadenas:
;(() => { type entries = { [key: string]: string | number } const data: entries[] = [ { key: 2, number: 4 }, { key: 'bbb', number: 1 }, { key: 'aaa', number: 2}, { key: 1, number: 3} ] const index = 'key' const order: entries[] = data.sort((a, b) => `${a[index]}`.localeCompare(`${b[index]}`)) console.log(order) })()O bien, verifique el tipo de los valores:
;(() => { type entries = { [key: string]: string | number } const data: entries[] = [ { key: 'bbb', number: 1 }, { key: 'aaa', number: 2} ] const index = 'key' const order: entries[] = data.sort((a, b) => { const valA = a[index]; const valB = b[index]; return typeof valA === 'number' && typeof valB === 'number' ? valA - valB : `${valA}`.localeCompare(`${valB}`); }); console.log(order) })()Según sus comentarios, creo que está diciendo que sabe que a[index] siempre será una cadena debido a la lógica de su código, pero el problema es que TypeScript no lo sabe (porque la firma del objeto dice que puede ser una cadena o un número).
Para tranquilizar a TypeScript, tiene al menos un par de opciones:
function assertIsString(value: any): asserts value is string { if (typeof value !== "string") { throw new Error(`Expected a string, but got ${typeof value}`); } }Después:
const order: entries[] = data.sort((a, b) => { const avalue = a[index]; const bvalue = b[index]; assertIsString(avalue); assertIsString(bvalue); return avalue.localeCompare(bvalue); });Esto también tiene la ventaja de generar un error explícito si el código no garantiza que el nombre de la propiedad sea solo el nombre de una propiedad de cadena.
Si no desea la sobrecarga (muy mínima) de la función de aserción, simplemente puede anular TypeScript:
const order: entries[] = data.sort((a, b) => (a[index] as string).localeCompare(b[index] as string));Si sabe que sus objetos son homogéneos, puede combinar los dos enfoques, por ejemplo, usando la función de aserción solo en la primera entrada:
if (data.length > 1) { assertIsString(data[0][index]); } const order: entries[] = data.sort((a, b) => (a[index] as string).localeCompare(b[index] as string)); De esa manera, obtiene un buen error explícito, pero no tiene la sobrecarga de verificar durante la sort .
Su TS no sigue las pautas de TS.
Aquí hice una función sortByProp , que usa keyof para que pueda seleccionar fácilmente la propiedad que desea usar para su clasificación.
Puedes probar el código aquí: Playground .
Cuando descubra cómo filtrar los tipos de propiedades, actualizaré el código nuevamente.
interface Entrie { key: string; number: number | string; random?: boolean; } // Used so I can filter the keys by the type I want. In this case string | number; type FilteredKeys<T, U> = { [P in keyof T]: T[P] extends U ? P : never }[keyof T]; const data: Entrie[] = [ { key: 'bbb', number: 1 }, { key: 'aaa', number: 3 }, { key: 'aba', number: 2 } ]; function sortByProp<T, F>(arr: T[], prop: FilteredKeys<T, F>) { if (typeof prop == 'string') return arr.sort((a, b) => { if (a[prop] < b[prop]) return -1; if (a[prop] > b[prop]) return 1; return 0; }); return arr.sort((a: any, b: any) => a[prop] - b[prop]); } console.log("SortByKey:", sortByProp<Entrie, string | number>(data, "key")); console.log("SortByNumber:", sortByProp<Entrie, string | number>(data, "number"));