Typescript: Der Compiler meldet bei Verwendung des Spread-Operators fälschlicherweise eine Nichtübereinstimmung der Parameter- / Aufrufzielsignatur

Erstellt am 3. Aug. 2015  ·  55Kommentare  ·  Quelle: microsoft/TypeScript

Der Compiler meldet bei Verwendung des Spread-Operators fälschlicherweise eine Nichtübereinstimmung zwischen Parameter und Aufrufzielsignatur. Einfügen dieses Codes in den TypeScript-Spielplatz:

function foo(x: number, y: number, z: number) { }
var args = [0, 1, 2];
foo(...args);

erzeugt diesen Fehler:

Supplied parameters do not match any signature of call target.
Bug Fixed

Hilfreichster Kommentar

@ jpike88 Ich weiß, dass du es gut meinst, aber du
von TS wie du 🕺

Alle 55 Kommentare

Und das Gleiche gilt, wenn args vom Typ any

function foo(x: number, y: number, z: number) { }

function bar(...args) {
    foo(...args); // Supplied parameters do not match any signature of call target.
}

Dies ist derzeit beabsichtigt. aber wir sollten es uns noch einmal überlegen.

@smashdevcode , @tjoskar , ich suche nach realen Verwendungsmöglichkeiten dieser Funktion. Erwarten Sie insbesondere, Arrays oder Tupel (oder beides vielleicht) zu verbreiten? Hier sind einige Spielzeugbeispiele:

//// arrays ////
var a1: number[] = [1,2];
var a2: number[] = [1,2,3];
var a3: number[] = [];
function twoNumbersOrSo(n?: number, m?: number) {
  return (n || -1) * (m || -1);
}
function howManyNumbersLessOne(n?: number, ...ns: number[]) {
    return ns.length;
}

//// tuples ////
var t1: [number, string] = [1, "foo"];
var t2: [number, string, string] = [1, "foo", "bar"];
function veryTraditional(n: number, s: string) {
    for (let i = 0; i < n; i++) {
        console.log(s);
    }
}
function moreInteresting(n: number, s: string, ...rest: string[]) {
    veryTraditional(n, s);
    console.log(rest);
}

Jetzt kann jeder von a1,a2,a3 auf die ersten beiden Funktionen angewendet werden, und jeder von t1,t2 kann auf die zweiten beiden angewendet werden.

Beachten Sie, dass mit Arrays:

  1. Haben Sie einen einzigen Typ, daher müssen alle Parameter vom gleichen Typ sein. (Ich denke, das könnte any .)
  2. Die Länge der Arrays ist zur Kompilierungszeit nicht bekannt, daher müssen die Parameter optional sein. Und das Verbreitungsargument muss das letzte sein (wie heute).

Mit Tupeln:

  1. Der Wert muss irgendwann eine Typanmerkung haben. Andernfalls wird es als Array interpretiert (z. B. (number | string)[] nicht [number, number, string] ). Im Spielzeugcode werden dadurch Einsparungen bei der Kürze beseitigt, aber in einem realen Projekt, das bereits viele Schnittstellen definiert, ist dies möglicherweise in Ordnung.
  2. Die Länge ist im Voraus bekannt, sodass ein Tupel auf jede Art von Parameterliste angewendet werden kann.

Hallo @sandersn ,

Entschuldigung für die späte Antwort. Ich habe deine Antwort total verpasst.
Ich verwende nur Arrays mit einem Datentyp, daher ist die Tupelversion für mich nicht sehr interessant.
Ich denke jedoch, dass die Spread-Operation in Funktionsaufrufen jedes iterierbare Objekt akzeptieren sollte. Also sollte es zB funktionieren. map und set , und in diesen Fällen müssen wir eine Schnittstelle erstellen, die mit dem Spread-Operator zusammenarbeitet, auch wenn die Daten mehrere Datentypen enthalten (z. B. new Set (). Add (1) .add ('string')). Aber das ist vielleicht ein anderes Ticket?

Beispiel mit Set und der Spread-Operation (funktioniert in Chrome 46)

function foo(...args) {
    console.log(...args);
}
var bar = new Set().add(1).add(2);

foo(...bar); // 1, 2

Ja, es ist ein separates Problem. Typescript unterstützt bereits Arrays als Typ für Ruheparameter, was generisch gemacht werden müsste. Wir diskutieren nur, wo Spread-Argumente verwendet werden könnten.

Möchten Sie Ihre homogenen Arrays / Sets an Funktionen mit Restparametern oder an Funktionen mit einer festen Anzahl von Argumenten übergeben? Ich hätte gerne mehr Details zu dem, was Sie anrufen würden.

Zunächst einmal habe ich heute Morgen mit dem Spread-Operator herumgespielt und die meisten meiner Anwendungsfälle funktionieren in der neuesten Version von Typoskript [email protected] und auf http://www.typescriptlang.org/Playground einwandfrei. Das ist also keine große Sache mehr.

Jedoch,
Ich mag die Idee nicht, alle Parameter optional zu machen, nur um sie verwenden zu können
Spread Operator ABER ich verstehe das Problem; Die Länge der Arrays ist daher zur Kompilierungszeit nicht bekannt
Es ist unmöglich, die Parameter zur Kompilierungszeit zu überprüfen.

Ich denke jedoch, es wäre schön, wenn die Parameter nicht optional sein müssen und der Compiler
Überprüfen Sie nur, ob "Spread-Variable" vom richtigen Typ ist, z. Nummer[]

function fun1(x: number, y: number, z: number) {
    return x+y+z;
}

let arr1 = [1, 2, 3];
let arr2 = [1, 2];

fun1(...arr1);
fun1(...arr2); // OK since `arr2` is number[]
fun1(1, 2); // Should cause an error

Ist das möglich?

Der folgende Code funktioniert in der aktuellen Version von Typoskript, was bei der Behebung dieses Problems nicht der Fall war.

function fun1(...num) {
    return Math.max(...num);
}

function fun2(a1, ...num) {
    return Math.max(...num);
}

function fun3(a1, ...num) {
    return fun1(...num);
}

let arr = [1, 2, 3];

if (Math.random() < .5) {
    arr.push(1);
}

fun1(...arr);
fun2('first param', ...arr);
fun3('first param', ...arr);

Vielleicht ein realistischeres Beispiel (das heutzutage auch funktioniert):

const doSomeWork = ({title, genre, runtime}) => { console.log(title, genre, runtime); };

function fun1(...num) {
    num.forEach(doSomeWork);
}

const data = [{
    title: 'title',
    genre: ['action', 'drama'],
    runtime: 100
}];

fun1(...data);

: +1: Das gleiche Problem mit dem spezifischen Anwendungsfall des Konstruktors Date :

let dateNumberArray: Array<number> = [2015,11,11];
let myNewDate = new Date(...dateNumberArray);

Für uns verwenden wir Spreads, damit Benutzer die argv-Anforderungen in einer Basis-App konfigurieren können, ohne ihre eigenen Helfer erstellen zu müssen. Zum Beispiel:

{
  "yargv": [
    [
      "path",
      {
        "demand": true,
        "describe": "Source",
        "default": ".",
        "alias": "p"
      }
    ]
  ]
}
get appArgs() : { yargv: Array<Array<any>>, chdir: Array<boolean | string> } {
  return require(
    "../args.json"
  )
}

argv() : Promise<{}> {
  return new Promise((resolve) => {
    this.appArgs.yargv.forEach(v => yargs.option(...v))
    return resolve(yargs.usage("$0 [args]").help("h").
      alias("h", "help").argv)
  })
}

Ein Beispiel aus der Praxis mit der Funktion https://github.com/ReactiveX/rxjs, bei dem ich den this beibehalten möchte, wenn ich projectFn mit einer Pfeilfunktion aufrufe und Restparameter verwende die Argumente verbreiten.

getCombination() {
    return this.firstObservable
      .combineLatest(this.secondObservable, (...args) => this.projectFn(...args)));
  }

Derzeit muss ich tun

getCombination() {
    return this.firstObservable
      .combineLatest(this.secondObservable, (...args) => this.projectFn.apply(this, args)));
  }

FWIW, ein weiteres Beispiel aus der Praxis, versucht, eine Reihe von Objekten mithilfe von Merge from lodash tief zusammenzuführen:

import { merge } from 'lodash';

...
return merge(...arrayOfObjects);

Hier ist ein weiterer realer Anwendungsfall. Wir versuchen, Code umzugestalten, der so aussieht:

return Observable
      .forkJoin(a, b)
      .map(([current, past]) => this.mergeData(current, past));

In die etwas elegantere:

return Observable
      .forkJoin(a, b)
      .map(data => this.mergeData(...data));

Die Verwendung des Spread-Operators löst jedoch den Signatur-Mismatch-Fehler aus.

Ein anderes Beispiel

class Parent {
    constructor(a, b, c){

    }
}

class Child extends Parent {
    constructor(d, ...args) {
        super(...args);
    }
}

TS2346: Die angegebenen Parameter stimmen nicht mit der Signatur des Anrufziels überein.

Gleiches Problem mit Überladungsmethoden:

listen(port: number, hostname?: string, backlog?: number, listeningListener?: Function): Server;
listen(port: number, hostname?: string, listeningListener?: Function): Server;
listen(port: number, backlog?: number, listeningListener?: Function): Server;
listen(port: number, listeningListener?: Function): Server;
listen(path: string, backlog?: number, listeningListener?: Function): Server;
listen(path: string, listeningListener?: Function): Server;
listen(options: ListenOptions, listeningListener?: Function): Server;
listen(handle: any, backlog?: number, listeningListener?: Function): Server;
listen(handle: any, listeningListener?: Function): Server;

während als genannt

myListen(...args) {
    listen(...args);
}

dann: _ [ts] Die angegebenen Parameter stimmen nicht mit der Signatur des Anrufziels überein.

Im Moment ist es also unmöglich, so etwas zu tun:

export type DateProp = Date | (string|number)[];

const setDate = (value: DateProp): Date => (
    isDate(value) ? value : new Date(...value)
);

Da es Fehler erzeugt:

Die angegebenen Parameter stimmen nicht mit der Signatur des Anrufziels überein.

Auch wenn der Wert die korrekten Parameterwerte für Datumskonstruktoren enthält

Auch wenn der Wert die korrekten Parameterwerte für Datumskonstruktoren enthält

Das ist richtig, das ist es, was dieses Problem verfolgt.

Ich fühle mich verpflichtet zu fragen, ob ich irgendwie helfen / beitragen kann. Wenn jetzt, können Sie erläutern, warum genau dies in diesem Fall einen Fehler verursacht? Kann es irgendwie durch config übersprungen werden?

Ich war tatsächlich von diesem Problem betroffen, als ich eine .js -Datei in .ts . Wie auch immer, ein Beispiel für "nicht alle gültigen JS sind gültige TS".

Derzeit tritt dieses Problem bei der Implementierung des Beispiels für

import {css} from 'styled-components';

export const Breakpoints = {
    tablet: 580,
    desktop: 800,
};

export type BreakpointLabels = keyof typeof Breakpoints;

export const media = Object.keys(Breakpoints).reduce((mediaQueries, label: BreakpointLabels) => (
    {
        ...mediaQueries,
        [label]: (...args: any[]) =>
            css`
                <strong i="7">@media</strong> (max-width: ${Breakpoints[label]}px) {
                    ${css(...args)}
                      ^^^^^^^^^^^^ Supplied parameters do not match any signature of call target.
                }
            `
    }
), {});

Meine Problemumgehung besteht darin, css.call , was zumindest mit den Argumenten vom Typ any[] funktioniert:

import {css} from 'styled-components';

export const Breakpoints = {
    tablet: 580,
    desktop: 800,
};

export type BreakpointLabels = keyof typeof Breakpoints;

export const media = Object.keys(Breakpoints).reduce((mediaQueries, label: BreakpointLabels) => (
    {
        ...mediaQueries,
        [label]: (...args: any[]) =>
            css`
                <strong i="13">@media</strong> (max-width: ${Breakpoints[label]}px) {
                    ${css.call(this, ...args)}
                }
            `
    }
), {});

gleiche Beispiele, mein Beispiel ist grundlegende Zusammenführungsfunktion:

merge(target: T, ...sources: T[])

Gibt es eine Problemumgehung, um diesen Fehler mindestens pro Datei stummzuschalten?

Eigentlich würde ich gerne Gründe sehen, warum diese ES6-Möglichkeit durch Design oder Pläne gebrochen wird, wenn sie möglicherweise überdacht wird. @mhegazy bitte kommentieren.

Wir haben kürzlich eine Änderung vorgenommen, um die Verteilung auf Anrufausdrücke zu ermöglichen, wenn das Ziel optional ist (siehe https://github.com/Microsoft/TypeScript/pull/15849). Die Änderung behandelt eine Spread-Ausgabe als unendliche Menge optionaler Argumente.

Mit dieser Änderung ist hier der aktuelle Status in Beispielen:

declare var args: number[];

function foo(x?: number, y?: number, z?: number) { }
foo(...args);     // OK
foo(2, ...args);  // OK

function bar(...args: number[]) { }
bar(...args);     // OK
bar(2, ...args);  // OK

function baz(x: number, y: number, z: number) { }
baz(...args);     // still not allowed

Im letzten Beispiel kann der Compiler nicht überprüfen, ob args die erforderlichen baz args erfüllt, da die Länge von args nicht statisch validiert werden kann.

In diesem Bereich müssen Tupel mit bekannter Größe Funktionen mit erforderlichen Argumenten gleicher Länge erfüllen. Dies ist jedoch nicht so einfach, wie es sich anhört. z.B:

function baz(x: number, y: number, z: number) { }
var tuple: [number, number, number] = [1, 2, 3];
baz(...tuple);     // should be allowed

Ich habe das gleiche Problem wie @devrelm

Ich habe immer noch Probleme damit, was ist los?

class A {
    constructor(message: string, test?: any) {
        console.log('A constructor called');
    }
}

class B extends A {
    constructor(...spread) {
        super('a', ...spread);
    }
}

Es gibt einen Fehler auf dem Typoskript-Spielplatz

@owlcode Dies ist in TS 2.4, aber der Spielplatz ist in TS 2.3 bis nach 2.4 Releases.

Dies scheint in TS 2.4 behoben zu sein.

export function log(...args: any[]) {
    console.log(...join(args.map(formatDevTools),' '));
}

Allerdings scheint es ein neuer Fehler in TS 2.4 zu sein:

TS2461: Typ 'Iterable'ist kein Array-Typ.

Passiert, wenn Sie versuchen, eine iterable ( [...obj] ) zu verbreiten.

Und doch ein anderer Fehler (glaube ich) in 2.4.1-insiders.20170615 den ich noch nicht herausfinden konnte.

Ich weiß auch nicht, wie ich das umgehen soll. Das Casting unserer Arrays auf any[] wird nicht helfen.


Egal, wir können die Definition von Console .

interface _Console {
    assert(test?: boolean, message?: string, ...optionalParams: any[]): void;
    clear(): void;
    count(countTitle?: string): void;
    debug(...optionalParams: any[]): void;
    dir(value?: any, ...optionalParams: any[]): void;
    dirxml(value: any): void;
    error(...optionalParams: any[]): void;
    exception(message?: string, ...optionalParams: any[]): void;
    group(groupTitle?: string): void;
    groupCollapsed(groupTitle?: string): void;
    groupEnd(): void;
    info(...optionalParams: any[]): void;
    log(...optionalParams: any[]): void;
    msIsIndependentlyComposed(element: Element): boolean;
    profile(reportName?: string): void;
    profileEnd(): void;
    select(element: Element): void;
    table(...data: any[]): void;
    time(timerName?: string): void;
    timeEnd(timerName?: string): void;
    trace(...optionalParams: any[]): void;
    warn(...optionalParams: any[]): void;
}

Und dann das console Objekt umwandeln:

export function log(...args: any[]) {
    (console as _Console).log(...join(args.map(formatDevTools),' '));
}

Das muss erstmal reichen.

Ich habe dafür eine PR bei # 18004 eröffnet.

Im Moment muss ich "noStrictGenericChecks" in meiner tsconfig aktivieren.

Im Moment muss ich "noStrictGenericChecks" in meiner tsconfig aktivieren.

Ich bin mir nicht sicher, wie das mit diesem Fehler zusammenhängt ...

Ich bin mir nicht sicher, wie das mit diesem Fehler zusammenhängt.

@mhegazy Diese Einstellung deaktiviert alle Funktionssignaturen, sodass ich Argumente mit dem Spread-Operator übergeben kann, ohne die Anzahl der Argumente

Das ist nicht das, was --noStrictGenericChecks tun. Weitere Informationen finden Sie unter https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#stricter -checking-for-generic-functions

Ich bin gerade darauf gestoßen.

Obwohl ich verstehen kann, dass es schwierig ist, sollten zumindest "überschüssige" Argumente identifiziert werden können.

@sandersn aus der Perspektive der realen Welt haben wir eine API so umgestaltet, dass sie entweder ein einzelnes Argument oder ein Array akzeptiert, anstatt ein Restargument zu verwenden. Situationen, in denen wir so etwas wie foo(value, ...otherValues) wurden also nicht als Fehler identifiziert. Insbesondere scheint dies wirklich gefährlich :

declare const foo: (value: string) => string;

foo('bar', 'qat'); // Expected 1 arguments, but got 2.

foo('bar', ...['qat']); // No error

@kitsonk kannst du dafür einen separaten Bug öffnen? Es wäre einfacher, es isoliert zu betrachten, und es ist wahrscheinlich eher eine kleine Änderung der bestehenden Regeln als eine komplexe Änderung.

Selbst wenn ich die Länge überprüfe, erhalte ich immer noch den folgenden Fehler:

function myFunc(...args: any[]) {
  if(args.length > 0) {
    otherFunc(...args)
  }
}

Bearbeiten:

Um Verwirrung zu beseitigen, wurde ich von hier umgeleitet, wo es heißt:

Erwartet 2 Argumente, aber mindestens 0

Dieses Problem wurde als Duplikat dieses Problems angesehen.

In meinem Fall konnte ich meinen Fehler beheben (eher wie ein Hack als ein Fix), indem ich zuließ, dass nichts an die Methode übergeben wurde, indem ich function otherFunc() als einen der Initialisierer hinzufügte:

function otherFunc()
function otherFunc(arg1: string)
function otherFunc(arg1: string, arg2: number)
function otherFunc(...args: any[]) {
  // Do stuff
}

Diese Problemumgehung wird in 2.7 unterbrochen

Wie ist das noch ein Problem? Spread-Operatoren sind eine perfekt kompatible Methode, um in JS zu arbeiten, daher sollte TS dies ordnungsgemäß berücksichtigen.

@ jpike88 Ich weiß, dass du es gut meinst, aber du
von TS wie du 🕺

Ein weiterer schöner Testfall (eigentlich eine Regression von TS 2.7) ist folgender:

class NonEmptyArray<T> extends Array<T> {
        0: T;
}

function c(firstArg: string, ... plainValues: string[]): string;
function c(): undefined;
function c(...values: string[]): string | undefined {
        if (!values.length) {
                return undefined;
        }
        return "";
}


function d(): NonEmptyArray<string> {
        return [""];
}

function y(): string | undefined {
        return c(...d());
}

Es produziert

test.ts (20,9): Fehler TS2557: Mindestens 0 Argumente erwartet, aber 0 oder mehr.

Ein weiteres Problem, auf das ich in Bezug auf den Spread-Operator gestoßen bin:

const get = (id?: string = '1231254', bar?: Bar, baz?: Baz) => {...}
const foo: [undefined, Bar, Baz] = [undefined, bar, baz]

get(...foo) // [ts] Argument of type 'Bar | Baz' is not assignable to parameter  
of type 'string'.
Type 'Baz' is not assignable to type 'string'

Natürlich habe ich in ES6 kein Problem damit, ein Array mit undefinierten Argumenten zu verbreiten und die undefined richtig zu übergeben, aber es scheint, als würde TS die undefined -Werte einfach ignorieren, wenn er sie verbreitet.

Ein weiteres Beispiel aus der Praxis, bei dem die korrekte Behandlung des Spread-Operators beim Funktionsaufruf sehr hilfreich ist:
Ich habe Funktionen, die vom NSwagStudio - API - Generator für C # WebApi generiert wurden und dieselben Parameter haben (GET - Parameter sind in der Struktur definiert).

Die generierte Funktion sieht folgendermaßen aus:

export interface ITableApiClient {
    getTableInfo(objnam: string | null, objsch: string | null | undefined, dbnam: string | null, conid: number | undefined): Promise<FileResponse | null>;
    otherFunction(objnam: string | null, objsch: string | null | undefined, dbnam: string | null, conid: number | undefined): Promise<string | null>;
}

Ich möchte es mit Syntax aufrufen

   get tableIdentification() {
       return [this.name, this.schema, this.databaseName, this.serverConnectionId];
   }
...
   return apiClient.getTableInfo(...this.tableIdentification);

   // this doesn't help, still compile-time error
   // return apiClient.getTableInfo(...(this.tableIdentification as any));

weil es mehr Funktionen mit genau denselben Parametern gibt (weil sie aus derselben Parameterklasse generiert werden, die im C # -Backend definiert ist). Jetzt muss ich den Hauptteil der Eigenschaft tableIdentification n-mal in jede Verwendung kopieren

@smashdevcode Für mich bestand die Lösung darin, @ ts-ignore hinzuzufügen

function foo(x: number, y: number, z: number) { }
var args = [0, 1, 2];
// @ts-ignore
foo(...args);

Beispiel hier: https://stackblitz.com/edit/typescript-ecymei?embed=1&file=index.ts

@ darekf77 , oder wenn Sie sagen, dass args ein Tupel ist:

function foo(x: number, y: number, z: number) { }
const args: [number, number, number] = [0, 1, 2];
foo(...args);

ts-Spielplatz

@ darekf77 , oder wenn Sie sagen, dass args ein Tupel ist:

function foo(x: number, y: number, z: number) { }
const args: [number, number, number] = [0, 1, 2];
foo(...args);

Dies funktioniert für mich, wenn ich Typoskript in der Befehlszeile kompiliere, aber nicht, wenn Visual Studio Code das Typoskript während der Eingabe überprüft. Ich bin mir nicht sicher, was die Trennung hier ist.

Ich sehe, es ist über ein Jahr her, seit Jayphelps erwähnt hat, dass dies in einer kommenden Version behoben wird, aber dieses Problem tritt immer noch auf. Gibt es ein Update dazu? Ich weiß, dass ich @ ts-ignore verwenden könnte, dies entfernt jedoch ALLE Typoskriptnachrichten für die Funktion und ist daher keine wirklich geeignete Lösung.

@TidyIQ, wie von tjoskar erwähnt, typisiere dein Spread-Array als Tupel.

Das Array hat keine feste Länge, daher funktioniert das nicht.

Ein Typecast funktioniert für den Compiler, auch wenn er nicht korrekt ist. Ich bin damit einverstanden, dass es nicht die beste Lösung ist.

(Diese Frage sollte wahrscheinlich in den Stapelüberlauf (oder ähnlich) verschoben werden.)

@TidyIQ , wenn das Array keine

function foo(x: number, y: number, z: number) { }
const args = arrayOfSomeLength;
foo(...args); // Error: The compiler do not know if there is 3 or 2 or 1 or 50 elements in the array. 

Wenn das Array also eine dynamische Länge hat, sollten die Argumente auch folgende sein:

function foo(...args: number[]) { }
const args = arrayOfSomeLength;
foo(...args);

Spielplatz

Leider funktioniert das auch nicht, da einige meiner Argumente eine feste Länge haben.

Wenn ich nur den Code poste, wird das Problem möglicherweise angezeigt.

interface IF_Object {
  [key: string]: object;
}
//  VALUE SHOULD BE REQUIRED BUT MADE OPTIONAL TO BYPASS TYPESCRIPT ERROR
interface IF_SetKV {
  (key: string, value?: any): Function;
}
//  VALUE SHOULD BE REQUIRED BUT MADE OPTIONAL TO BYPASS TYPESCRIPT ERROR
interface IF_CreateState {
  (key: string, value?: any, ...more: string[]): Function;
}

const setKV: IF_SetKV = (key, value) => (object: IF_Object = {}) => ({
  ...object,
  [key]: value
});

const createState: IF_CreateState = (key, value, ...more) => (
  object: IF_Object = {}
) =>
  more.length === 0
    ? setKV(key, value)(object)
    : setKV(key, createState(value, ...more)(object[key]))(object);

// TYPESCRIPT ERROR OCCURS HERE. CAN ONLY BE REMOVED WITH TS<strong i="7">@IGNORE</strong>
const newState = createState(...stateList, action.payload.value)(reduced);

@TidyIQ , ich bin nicht sicher, ob ich folge. Was passiert, wenn stateList ein leeres Array ist? In diesem Fall wird action.payload.value als key ?

Sollte das nicht funktionieren:

const createState = (...args: string[]) => (object: IF_Object = {}) => {
  const [key, value, ...rest] = args;
  if (rest.length === 0) {
    setKV(key, value)(object)
  } else {
    setKV(key, createState(value, ...rest)(object[key]))(object);
  }
}

Cast, um den Wert als ParameterType der Funktion zu verbreiten, an die Sie Argumente übergeben.

const add = (a: number, b: number) => a + b

const values = {
  a: 1,
  b: 2,
}

type AddParams = Parameters<typeof add>

add(...(values) as AddParams)

@ mateja176 Dies scheint auch zu funktionieren und könnte für einige Anwendungsfälle besser geeignet sein.

add(...(values as [number,number]))

oder

add(...(values as [any,any]))

Ich verwende hierfür einen Hilfstyp, um beliebige Arrays zu verarbeiten, ohne sie explizit eingeben zu müssen:

type NonEmptyArray<T extends any[]> = T extends (infer U)[]
  ? [U, ...U[]]
  : never

Es kann folgendermaßen verwendet werden:

add(...values as NonEmptyArray<typeof values>)


Detaillierte Erklärung für Anfänger

Die folgende Aufteilung des Typs NonEmptyArray erklärt im Detail, wie es funktioniert:

# | Teil | Erläuterung
- | - | -
(1) | type NonEmptyArray | Der Name des Hilfstyps
(2) | <T | Der Hilfstyp verwendet einen generischen Typparameter T .
(3) | extends any[]> | Um von der Typprüfung akzeptiert zu werden, muss dieser Typparameter T ein Array-Typ sein.
(4) | T extends (infer U)[] ? | Unser Hilfstyp ist ein bedingter Typ, der prüft, ob T tatsächlich ein Array-Typ ist.
Wir haben T als Array-Typ in (3) deklariert, daher ist diese Bedingung immer erfüllt. Wenn wir jedoch die Bedingung verwenden, können wir TypeScript infer den Typ des Arrays T besteht aus und wir nennen diesen Typ U .
(5) | [U, ...U[]] | Jetzt können wir den resultierenden Typ zusammenstellen: ein Array, in dem der erste Eintrag vom Typ U und die verbleibenden (0 oder mehr) Einträge ebenfalls vom Typ U .
Aufgrund dieser speziellen Tupelnotation ist TypeScript bekannt, dass mindestens ein Element vorhanden ist.
(6) | : never | Dies ist nur erforderlich, um die Bedingung syntaktisch zu vervollständigen. Denken Sie daran: Die Bedingung ist nur ein Trick, um den Typ U zu extrahieren, und sie besteht immer. Daher können wir den Zweig "else" ignorieren, indem wir never .


Wenn wir das tun ...

ts const values = [1,2,3] add(...values as NonEmptyArray<typeof values>)

... wird Folgendes passieren:

  • typeof values ist der Typ, den TypeScript für das Array values , bei dem es sich um ein Array von Zahlen handelt: number[] .
  • Das heißt, wir übergeben number[] als T . (2/3)
  • T ist in der Tat ein Array-Typ, daher können wir daraus U ableiten, was number . (4)
  • Jetzt, da wir wissen, dass U ein number , ergeben wir den Typ [number, ...number[]] . (5)

@tjoskar ändere deinen Code in

function foo(x: number, y: number, z: number, f: number) { }
const args: [number, number, number] = [0, 1, 2];
foo(...args, 3);

( Spielplatz )
Und der Fehler ist zurück.

Wenn Sie die letzte Zeile in foo(3, ...args); ändern, tritt überraschenderweise kein Fehler auf.

Ich habe das Gefühl, dass das immer noch nicht funktioniert. Hier ist mein Beispiel

onSetValue={(...args: Parameters<typeof props.onSetValue>) => {
    setLanguage(null);
    props.onSetValue(...args); // Expected 2 arguments, but got 0 or more.
  }}

Es sollte eigentlich keine Rolle spielen, was der Typ von props.onSetValue , da ich einfach den Parametertyp nehme und ihn an die Funktion übergebe, von der ich den Typ erhalten habe, und es immer noch den Fehler Expected 2 arguments, but got 0 or more. .

Spielplatz Link

Hier ist eine reduzierte Form des Beispiels von @ Haaxor1689 :
Spielplatz Link

Ich kann es immer noch nicht zum Laufen bringen

Dies ist meine vorübergehende Problemumgehung

class Board {
  private events: Events

  public on(...args: Parameters<this['events']['on']>) {
    this.events.on.call(this.events, ...args)
  }
}
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen