TypeScript Array-unionstype i funksjonsparametere


Jeg har en unionstype av et array med forskjellige spesifikke lengder:

[ number ] | [ number, number ] | [ number, number, number, number ]

Som du kan se, er det krav til et array med ett element, to elementer eller fire elementer.

Jeg prøver å lage et objekt som inneholder en funksjon med en av disse lengdene. Hvordan skriver jeg typedefinisjonen for å tillate dette?

TS lekeplass

Eksempel :

const people: {
    name: string,
    address: Address,
    work: (numbers: [ number ] | [ number, number ] | [ number, number, number, number ]) => any
}[] = [
    {
        name: "Bob",
        address: new Address(),
        work: function(numbers: [ number ]): number {
            // Implementation returning number
        }
    },
    {
        name: "Ashley",
        address: new Address(),
        work: function(numbers: [ number, number, number, number ]): boolean {
            // Implementation returning boolean
        }
    },
    {
        name: "Michael",
        address: new Address(),
        work: function(numbers: [ number, number ]): number {
            // Implementation returning number
        }
    },
]

For øyeblikket gir det meg feilen:

Type '(numbers: [number]) => number' kan ikke tildeles typen '(numbers: [number] | [number, number] | [number, number, number, number]) => any'. Typene til parameterne 'numbers' og 'numbers' er inkompatible. Type '[number] | [number, number] | [number, number, number, number]' kan ikke tildeles typen '[number]'. Type '[number, number]' kan ikke tildeles typen '[number]'. Kilden har 2 element(er) men målet tillater bare 1.ts(2322)

------- Rediger -------

Jeg har brukt et forslag fra kommentarene og gjort alle mulige kall til separate funksjonsunioner i stedet for en array-union:

const people: {
    name: string,
    address: Address,
    work: ((numbers: [ number ]) => any) | ((numbers: [ number, number ]) => any) | ((numbers: [ number, number, number, number ]) => any)
}[] = [

Når du prøver å nå kalle en funksjon fra dette arrayet:

 people[1].work([2, 8, 6, 4])

Den kaster følgende feil nå:

I VSCode fant jeg ut at dette er grunnen:

"Krysset ''[nummer] & [nummer, nummer] & [nummer, nummer, nummer, nummer]'' ble redusert til ''aldri'' fordi egenskapen ''length'' har motstridende typer i noen bestanddeler."

Svar #1

OPPDATERT Du må bruke bivarians her

class Address { }

type Tuple<
  N extends number,
  Item = number,
  Result extends Array<unknown> = [],
  > =
  (Result['length'] extends N
    ? Result
    : Tuple<N, Item, [...Result, number]>
  )


interface WorkFn {
  work(numbers: Tuple<1> | Tuple<2> | Tuple<4>): any
}

interface Person extends WorkFn {
  name: string,
  address: Address,
}

const people: Person[] = [
  {
    name: "Bob",
    address: new Address(),
    work(numbers: Tuple<1>) {
      const [myNumber] = numbers;

      return myNumber * 6
    }
  },
  {
    name: "Ashley",
    address: new Address(),
    work: function (numbers: Tuple<4>): boolean {
      const [myNumber, anotherNumber, someNumber, replaceNumber] = numbers;

      return myNumber === anotherNumber && someNumber === replaceNumber;
    }
  },
  {
    name: "Michael",
    address: new Address(),
    work: function (numbers: Tuple<2>): number {
      const [myNumber, anotherNumber] = numbers;

      return myNumber * anotherNumber;
    }
  },
]

TypeScript-lekeplass

Her kan du finne forskjellen mellom metodeltype og pilfunksjonstype, og om bivarians

Vær også oppmerksom på at det ikke er 100% trygt

Svar #2

Mitt forslag ville være å bruke en generell funksjon for å lage people-arrayen.

function createPeople<
  T extends {
    name: string,
    address: Address,
    work: ((numbers: [ number ]) => any) | ((numbers: [ number, number ]) => any) | ((numbers: [number, number, number, number]) => any)
  }[]
>(p: [...T]){
  return p
}

const people = createPeople([
  {
    name: "Bob",
    address: new Address(),
    work: function(numbers: [ number ]): number {
      const [ myNumber ]: [ number ] = numbers;

      return myNumber * 6
    }
  },
  {
    name: "Ashley",
    address: new Address(),
    work: function(numbers: [ number, number, number, number ]): boolean {
      const [ myNumber, anotherNumber, someNumber, replaceNumber ]: [ number, number, number, number ]= numbers;

      return myNumber === anotherNumber && someNumber === replaceNumber;
    }
  },
  {
    name: "Michael",
    address: new Address(),
    work: function(numbers: [ number, number ]): number {
      const [ myNumber, anotherNumber ]: [ number, number ] = numbers;

      return myNumber * anotherNumber;
    }
  },
])

TypeScript vet nå hva callbacken til hver indeks er. Det gjør at de følgende kallene er strikt typet.

people[1].work([2, 8, 6, 4])
people[2].work([1, 2])

Lekeplass

Relaterte artikler