Tengo un caso simple que transforma una matriz de cadenas anidadas en una matriz de números anidados, sin aplanamiento
const strArr = ["1", "2", ["3", "4"], "5"]; function mapToNumber(item: string | Array<string>): number | Array<number> { if (Array.isArray(item)) { return item.map(mapToNumber); // line Hey } else { return Number(item); } } console.log(strArr.map(mapToNumber));
Sin embargo, TS me grita: Type '(number | number[])[]' is not assignable to type 'number | number[]'
. Luego cambié la línea Hey
para return item.map<number>(mapToNumber)
, no funciona. La sobrecarga de funciones vino a mi mente, lo intenté:
const isStringArray = (arr: any): arr is Array<string> => { return Array.isArray(arr) && arr.every(item => typeof item === "string"); } function mapToNumber(item: string): number; function mapToNumber(item: Array<string>): Array<number>; function mapToNumber(item: any) { if (isStringArray(item)) { return item.map<number>(mapToNumber); } else { return Number(item); } } console.log(strArr.map(mapToNumber));
Aunque agregué el protector de tipo personalizado, todavía no funciona.
La lógica es bastante simple, pero ¿cómo puedo definir el tipo correcto para este caso simple? El enlace del patio de recreo
Editar :
Probé los genéricos, todavía no funciona
function mapToNumber3<T extends string | Array<string>>(item: T): T extends string ? number : Array<number> { if (Array.isArray(item)) { return item.map(mapToNumber3); } else { return Number(item); } }
Más información sobre este problema de Github (básicamente, lamentablemente no hay una buena manera de hacer esto actualmente). Propuesta de lo que quieres aquí .
Creo que solo tienes que usar as
por ahora:
const strArr = ["1", "2", ["3", "4"], "5"];
function mapToNumber(item: string | string[]):number | number[]
{ if (typeof item === 'string') { return Number(item); }
else { return item.map(mapToNumber) as number[]; // Here }
} console.log(strArr.map(item => mapToNumber(item)));
Su ejemplo con sobrecarga técnicamente funciona si agrega una sobrecarga más. Depende de usted si cree que vale la pena todo el esfuerzo.
const strArr = ["1", "2", ["3", "4"], "5"]; const isStringArray = (arr: any): arr is Array<string> => { return Array.isArray(arr) && arr.every(item => typeof item === "string"); } function mapToNumber(item: string): number; function mapToNumber(item: string[]): number[]; function mapToNumber(item: string | string[]): number | number[]; // Add this function mapToNumber(item: any) { if (isStringArray(item)) { return item.map<number>(mapToNumber); } else { return Number(item); } } console.log(strArr.map(mapToNumber));
Para hacer eso, puede usar la cuantificación limitada por f :
const strArr = ["1", "2", ["3", "4"], "5"]; const mapToNumber = (item: string) => parseInt(item, 10) const isString = (item: unknown): item is string => typeof item === "string" const isStringArray = (arr: any): arr is Array<string> => Array.isArray(arr) && arr.every(isString) const map = < N extends number, Elem extends `${N}` | Array<Elem>, T extends Array<T | Elem>, >(arr: [...T]): Array<unknown> => { return arr.map((item) => { if (isStringArray(item)) { return item.map(mapToNumber); } if (Array.isArray(item)) { return map(item) } if (isString(item)) { return mapToNumber(item) } return item }) } const result = map(["1", "2", ["3", "4", ['6', ['7']]], "5"])
T extends Array<T | Elem>
- T es un genérico recursivo
El enfoque más seguro es devolver Array<unknown>
ya que no conoce la profundidad de la matriz Playground
Es relativamente fácil crear un tipo de estructura de datos recursiva en mecanografiado. Ver here , pero es difícil usarlo como un tipo de retorno en función
Intentaría simplemente definir un tipo: type NestedArray<T> = T | NestedArray<T>[]
para representar una matriz anidada de tipo T. Luego, simplemente escriba sus variables en sus funciones originales y debería funcionar.
type NestedArray<T> = T | NestedArray<T>[] const strArr: NestedAr ray<string> = ["1", "2", ["3", "4"], "5"]; const isStringArray = (arr: any): arr is Array<string> => { return Array.isArray(arr) && arr.every(item => typeof item === "string"); } function mapToNumber(item: NestedArray<string>): NestedArray<number> { if (isStringArray(item)) { return item.map(mapToNumber); } else { return Number(item); } } console.log(strArr.map(mapToNumber));