javascript - Array av blandede typer, men med minimum antall av en viss type i Array


La oss si at jeg vil håndheve streng typetolkning på et gitt array, slik at:

  • Det kan inneholde et hvilket som helst antall (dvs. null eller flere) number
  • Det må inneholde ett eller flere tall av string

...og rekkefølgen av number og string i arrayen ikke betyr noe, slik at følgende arrayer er gyldige:

  • ['foo', 1, 2, 3]
  • [1, 2, 'foo', 3]
  • ['foo']

Men følgende array er ugyldig:

  • [1, 2, 3] (fordi det trenger minst én string i arrayen)

Den nærmeste løsningen som nesten fungerte for meg, er å definere typen som:

/**
 * Array must contain:
 * - One or more string
 * - Zero or more numbers
 * @type
 */
type CustomArray = [string] & Array<number | string>

const a: CustomArray = ['foo', 1, 2, 3]; // Should pass (works as expected)
const b: CustomArray = [1, 2, 'foo', 3]; // Should pass (but doesn't with my code)
const c: CustomArray = ['string'];       // Should pass (works as expected)
const d: CustomArray = [1, 2, 3];        // Should fail (works as expected)

Men dette betyr at det første elementet i arrayen må være en streng, i stedet for å håndheve en minimumsantall på 1 gjennom hele arrayen. Du kan teste det på TypeScript Playground her.

Svar #1

Det finnes ingen måte å fortelle TypeScript at et array skal inneholde minst ett element av en bestemt type.

Det beste du kan gjøre er bare å lage et tall-/streng-array:

type CustomArray = (number | string)[];

Eller

type CustomArray = Array<number | string>;

Og deretter legg til de nødvendige kontrollene der du legger til eller leser data til/fra arrayen.

Svar #2

Ville dette være en idé?

/**
 * Array must contain:
 * - One or more string
 * - Zero or more numbers
 * @type
 */
console.clear();

const CustomArray = (arr: Array<string | number>) => {
  if (!arr.length) {
    return new TypeError("Invalid: array should contain at least one element");
  }

  const oneString = arr.filter(v => v.constructor === String).length;
  const wrongTypes = arr.filter(v => v.constructor !== Number && v.constructor !== String).length && true || false;
  
  if (wrongTypes) {
    return new TypeError(`Invalid: [${JSON.stringify(arr)}] contains invalid types`);
  }

  if (!oneString) {
    return new TypeError(`Invalid: [${JSON.stringify(arr)}] should contain at least one String value`);
  }
  
  return arr;
};

type MyArray = Array<string | number> | TypeError;

const a: MyArray = CustomArray(['foo', 1, 2, 3]); // Should pass (works as expected)
const b: MyArray = CustomArray([1, 2, 'foo', 3]); // Should pass (works as expected)
const c: MyArray = CustomArray(['string']);       // Should pass (works as expected)
const d: MyArray = CustomArray([1, 2, 3]);        // Should fail (works as expected)

const log = (v:any) => 
  console.log(v instanceof TypeError ? v.message : JSON.stringify(v));

log(a);
log(b);
log(c);
log(d);

Relaterte artikler