Typescript: Penyusun salah melaporkan parameter / memanggil ketidakcocokan tanda tangan target saat menggunakan operator penyebaran

Dibuat pada 3 Agu 2015  ·  55Komentar  ·  Sumber: microsoft/TypeScript

Kompilator salah melaporkan ketidakcocokan parameter / panggilan target tanda tangan saat menggunakan operator penyebaran. Menempelkan kode ini ke dalam TypeScript Playground:

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

menghasilkan kesalahan ini:

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

Komentar yang paling membantu

@ jpike88 Saya tahu maksud Anda baik, tetapi Anda mungkin tidak menyadari pernyataan seperti ini kontra produktif. Masalahnya ada di TODO mereka untuk pencapaian 2.7 atau 2.8. Ini mungkin tampak seperti hal yang jelas untuk diperbaiki, tetapi ketika Anda memiliki ribuan hal yang jelas untuk diperbaiki, Anda tidak dapat memperbaiki semuanya pada saat yang sama, dan terus meningkatkan hal-hal lain yang diharapkan darinya. Cara terbaik untuk menyuarakan suara Anda adalah dengan memberikan reaksi 👍 pada postingan asli di bagian atas. Bagaimanapun, ini adalah perangkat lunak gratis dan mereka tidak berhutang apapun kepada kita. Yang mengatakan, itu hanya pendapat saya, pengguna lain
TS sepertimu 🕺

Semua 55 komentar

Dan hal yang sama berlaku untuk args adalah any -type

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

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

ini saat ini sesuai desain. tapi kita harus mempertimbangkannya kembali.

@smashdevcode , @tjoskar , Saya sedang mencari beberapa penggunaan nyata dari fitur ini. Secara khusus, apakah Anda berharap untuk menyebarkan array atau tuple (atau keduanya mungkin)? Berikut beberapa contoh mainan:

//// 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);
}

Sekarang salah satu dari a1,a2,a3 dapat diterapkan ke dua fungsi pertama, dan salah satu dari t1,t2 dapat diterapkan ke dua fungsi kedua.

Perhatikan bahwa dengan array:

  1. Memiliki satu tipe, jadi semua parameter harus memiliki tipe yang sama. (Saya kira ini bisa jadi any .)
  2. Panjang array tidak diketahui pada waktu kompilasi, jadi parameternya harus opsional. Dan argumen penyebaran harus menjadi yang terakhir (seperti hari ini).

Dengan tupel:

  1. Nilainya harus memiliki anotasi tipe di beberapa titik. Jika tidak, ini akan diinterpretasikan sebagai sebuah array (misalnya (number | string)[] bukan [number, number, string] ). Dalam kode mainan, hal ini menghapus simpanan apa pun secara singkat, tetapi mungkin tidak masalah dalam proyek nyata yang sudah menetapkan banyak antarmuka.
  2. Panjangnya diketahui sebelumnya, sehingga tupel dapat diterapkan ke semua jenis daftar parameter.

Hi @sandersn,

Maaf atas tanggapan yang terlambat. Aku benar-benar melewatkan balasanmu.
Saya hanya menggunakan array dengan satu tipe data sehingga versi tuple tidak terlalu menarik bagi saya.
Namun saya pikir operasi penyebaran dalam pemanggilan fungsi harus menerima objek iterable apa pun. Jadi itu harus bekerja dengan mis. map dan set , dan dalam kasus ini kita perlu membuat antarmuka yang bekerja dengan operator penyebaran meskipun datanya berisi beberapa tipe data (mis. Set baru (). Add (1) .add ('string')). Tapi itu mungkin tiket lain?

Contoh dengan Set dan operasi penyebaran (bekerja di Chrome 46)

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

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

Ya, ini masalah terpisah. Ketik sudah mendukung array sebagai tipe untuk parameter istirahat, yang merupakan hal yang perlu dibuat generik. Kami hanya membahas di mana spread args bisa digunakan.

Apakah Anda ingin meneruskan array / set homogen Anda ke fungsi dengan parameter rest atau yang dengan jumlah argumen tetap? Saya ingin lebih detail tentang hal yang akan Anda telepon.

Pertama-tama, saya telah bermain-main dengan operator penyebaran pagi ini dan sebagian besar kasus penggunaan saya berfungsi dengan baik di versi terbaru skrip ketikan [email protected] dan di http://www.typescriptlang.org/Playground. Jadi ini bukan masalah besar lagi.

Namun,
Saya tidak suka ide untuk membuat semua parameter opsional hanya agar dapat digunakan
operator penyebaran TAPI saya mengerti masalahnya; panjang array tidak diketahui pada waktu kompilasi jadi
tidak mungkin untuk memeriksa parameter pada waktu kompilasi.

Namun, saya pikir akan lebih baik jika parameternya tidak perlu opsional dan kompilernya
hanya memeriksa bahwa "variabel-sebaran" adalah jenis yang benar misalnya. jumlah[]

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

Apakah itu mungkin?

Kode berikut berfungsi dalam versi skrip ketikan saat ini, yang tidak berfungsi saat masalah ini diatasi.

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);

Mungkin contoh yang lebih realistis (yang juga berfungsi saat ini):

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: Mengalami masalah yang sama, dengan kasus penggunaan khusus dari Date konstruktor:

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

Bagi kami, kami menggunakan spread untuk memungkinkan pengguna mengonfigurasi persyaratan argv di aplikasi dasar tanpa perlu membuat pembantu mereka sendiri. Sebagai contoh:

{
  "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)
  })
}

Contoh dunia nyata menggunakan fungsi CombinedLatest dari https://github.com/ReactiveX/rxjs di mana saya ingin mempertahankan nilai this saat memanggil projectFn menggunakan fungsi panah dan menggunakan parameter istirahat untuk menyebarkan argumen.

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

Saat ini saya harus melakukannya

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

FWIW, contoh dunia nyata lainnya sedang mencoba untuk menggabungkan array objek menggunakan merge from lodash:

import { merge } from 'lodash';

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

Berikut kasus penggunaan dunia nyata lainnya; kami mencoba untuk merefaktor kode yang terlihat seperti ini:

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

Menjadi yang sedikit lebih elegan:

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

Tetapi menggunakan operator penyebaran memicu kesalahan ketidakcocokan tanda tangan.

Contoh lain

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

    }
}

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

TS2346: Parameter yang diberikan tidak cocok dengan tanda tangan apa pun dari target panggilan.

Masalah yang sama dengan metode overload:

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;

saat dipanggil sebagai

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

lalu: _ [ts] Parameter yang diberikan tidak cocok dengan tanda tangan apa pun dari target panggilan._

Jadi untuk saat ini tidak mungkin melakukan hal seperti itu:

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

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

Karena menghasilkan kesalahan:

Parameter yang diberikan tidak cocok dengan tanda tangan apa pun dari target panggilan.

Bahkan jika nilai berisi nilai parameter kontraktor Tanggal yang benar

Bahkan jika nilai berisi nilai parameter kontraktor Tanggal yang benar

Itu benar, inilah yang dilacak oleh masalah ini.

Saya merasa berkewajiban untuk bertanya apakah saya dapat membantu / berkontribusi. Jika sekarang, dapatkah Anda menjelaskan mengapa sebenarnya itu menyebabkan kesalahan dalam kasus ini? Bisakah itu dilewati entah bagaimana melalui konfigurasi?

Saya benar-benar terkena masalah ini ketika saya mengganti nama file .js menjadi .ts . Bagaimanapun, salah satu contoh "tidak semua JS yang valid adalah TS yang valid".

Saat ini saya mengalami masalah ini dalam mengimplementasikan contoh template media komponen-gaya.

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.
                }
            `
    }
), {});

Solusi saya adalah menggunakan css.call , yang setidaknya berfungsi dengan args any[] -typed:

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)}
                }
            `
    }
), {});

masalah yang sama, contoh saya adalah fungsi penggabungan dasar:

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

apakah ada solusi untuk menonaktifkan kesalahan ini setidaknya per file?

Sebenarnya, saya ingin melihat alasan mengapa kemungkinan ES6 ini rusak karena disengaja atau rencana apa pun ketika mungkin dipertimbangkan kembali. @mhegazy tolong beri komentar.

Kami baru-baru ini membuat perubahan untuk memungkinkan penyebaran ke ekspresi panggilan jika target semuanya opsional (lihat https://github.com/Microsoft/TypeScript/pull/15849). Perubahan tersebut bekerja dengan memperlakukan keluaran menyebar sebagai kumpulan argumen opsional yang tak terbatas.

Dengan perubahan ini, berikut adalah contoh keadaan saat ini:

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

Pada contoh terakhir, kompilator tidak memiliki cara untuk memvalidasi bahwa args memenuhi baz , karena panjang args tidak dapat divalidasi secara statis.

Apa yang tersisa di area ini adalah untuk memungkinkan tupel dengan ukuran yang diketahui memenuhi fungsi dengan argumen yang diperlukan dengan panjang yang sama .. tetapi ini tidak sesederhana kedengarannya. misalnya:

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

Saya memiliki masalah yang sama seperti @devrelm

Saya masih mengalami masalah dengan ini, ada apa?

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

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

Ini memberikan kesalahan pada taman bermain ketikan

@owlcode ini ada di TS 2.4 tetapi taman bermainnya di TS 2.3 hingga setelah rilis 2.4.

Ini tampaknya diperbaiki di TS 2.4,

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

Namun, tampaknya ada bug baru di TS 2.4:

TS2461: Ketik 'Iterable'bukan tipe array.

Terjadi ketika Anda mencoba menyebarkan iterable ( [...obj] ).

Namun bug yang berbeda (menurut saya) di 2.4.1-insiders.20170615 yang belum saya temukan.

Saya juga tidak tahu bagaimana menyiasatinya. Mentransmisikan array kami ke any[] tidak akan membantu.


Tidak masalah, kita bisa memperbaiki definisi 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;
}

Dan kemudian melemparkan objek console :

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

Itu harus dilakukan sekarang.

Saya membuka PR untuk ini di # 18004.

Untuk saat ini, saya harus mengaktifkan "noStrictGenericChecks" di tsconfig saya.

Untuk saat ini, saya harus mengaktifkan "noStrictGenericChecks" di tsconfig saya.

Tidak yakin saya melihat bagaimana ini terkait dengan bug ini ...

Tidak yakin saya melihat bagaimana ini terkait dengan bug ini.

@mhegazy Pengaturan ini menonaktifkan semua tanda tangan fungsi, jadi saya bisa menyampaikan argumen menggunakan operator penyebaran tanpa mencocokkan jumlah argumen (setidaknya tidak secara statis)

bukan itu yang dilakukan --noStrictGenericChecks . Silakan lihat https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#stricter -checking-for-generic-functions

Saya baru saja menemukan ini.

Meskipun saya dapat memahami bahwa ini sulit, setidaknya argumen "berlebih" harus dapat diidentifikasi.

@sandersn dari perspektif dunia nyata kami melakukan refactoring API untuk menerima satu argumen atau larik, daripada menggunakan argumen istirahat. Jadi situasi di mana kami melewati sesuatu seperti foo(value, ...otherValues) tidak diidentifikasi sebagai kesalahan. Secara khusus ini tampaknya sangat berbahaya :

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

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

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

@kitsonk dapatkah Anda membuka bug terpisah untuk itu? Ini akan lebih mudah untuk dipertimbangkan secara terpisah, dan mungkin merupakan perubahan kecil pada aturan yang ada, daripada perubahan yang kompleks.

Bahkan jika saya memeriksa panjangnya, saya masih mendapatkan kesalahan ini:

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

Edit:

Untuk menjernihkan kebingungan, saya dialihkan dari sini , yang menyatakan:

Diharapkan 2 argumen, tetapi mendapat minimal 0

Masalah itu dianggap duplikat dari masalah ini.

Dalam kasus saya, saya dapat memperbaiki kesalahan saya (lebih seperti peretasan daripada perbaikan) dengan tidak mengizinkan apa pun untuk diteruskan ke metode dengan menambahkan function otherFunc() sebagai salah satu penginisialisasi seperti:

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

Solusi ini rusak di 2.7

Bagaimana ini masih menjadi masalah? Operator spread adalah cara yang sangat sesuai untuk bekerja di JS, jadi TS harus memperhitungkannya dengan benar.

@ jpike88 Saya tahu maksud Anda baik, tetapi Anda mungkin tidak menyadari pernyataan seperti ini kontra produktif. Masalahnya ada di TODO mereka untuk pencapaian 2.7 atau 2.8. Ini mungkin tampak seperti hal yang jelas untuk diperbaiki, tetapi ketika Anda memiliki ribuan hal yang jelas untuk diperbaiki, Anda tidak dapat memperbaiki semuanya pada saat yang sama, dan terus meningkatkan hal-hal lain yang diharapkan darinya. Cara terbaik untuk menyuarakan suara Anda adalah dengan memberikan reaksi 👍 pada postingan asli di bagian atas. Bagaimanapun, ini adalah perangkat lunak gratis dan mereka tidak berhutang apapun kepada kita. Yang mengatakan, itu hanya pendapat saya, pengguna lain
TS sepertimu 🕺

Kasus uji bagus lainnya (sebenarnya regresi TS 2.7) adalah ini:

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());
}

Itu menghasilkan

test.ts (20,9): error TS2557: Diharapkan setidaknya 0 argumen, tetapi mendapat 0 atau lebih.

Masalah lain yang saya temui tentang operator penyebaran:

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'

Tentu saja, di ES6 saya tidak punya masalah untuk menyebarkan array dengan tidak terdefinisi termasuk sebagai argumen dan meneruskan dengan benar undefined tetapi sepertinya TS mengabaikan nilai undefined ketika dia menyebarkannya.

Contoh dunia nyata lainnya, di mana penanganan yang benar dari operator penyebaran dalam panggilan fungsi akan banyak membantu:
Saya memiliki fungsi yang dihasilkan oleh NSwagStudio - generator API untuk C # WebApi, yang memiliki parameter yang sama (parameter GET ditentukan dalam struktur)

Fungsi yang dihasilkan terlihat seperti berikut:

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>;
}

Saya ingin menyebutnya dengan sintaks

   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));

karena ada lebih banyak fungsi dengan parameter yang sama persis (karena mereka dihasilkan dari kelas parameter yang sama yang ditentukan di backend C #). Sekarang saya harus menyalin badan properti tableIdentification n-kali ke dalam setiap penggunaan

@smashdevcode Bagi saya solusinya adalah menambahkan @ ts-ignore

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

contoh di sini: https://stackblitz.com/edit/typescript-ecymei?embed=1&file=index.ts

@ darekf77 , atau jika Anda mengatakan bahwa args adalah tupel:

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

ts-playground

@ darekf77 , atau jika Anda mengatakan bahwa args adalah tupel:

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

Ini berfungsi untuk saya ketika saya menyusun skrip ketikan di baris perintah, tetapi tidak saat Kode Visual Studio memvalidasi skrip ketikan saat saya mengetik. Saya tidak yakin apa pemutusannya di sini.

Saya melihat sudah lebih dari setahun sejak jayphelps menyebutkan bahwa ini akan diperbaiki dalam rilis yang akan datang, tetapi masalah ini masih terjadi. Apakah ada pembaruan tentang ini? Saya tahu saya bisa menggunakan @ ts-ignore namun ini menghapus SEMUA pesan skrip untuk fungsi jadi bukan solusi yang tepat.

@TidyIQ seperti yang disebutkan oleh tjoskar, ketik spread array Anda sebagai tupel.

Array tidak memiliki panjang tetap sehingga tidak akan berfungsi.

Sebuah typecast akan bekerja sejauh menyangkut compiler, bahkan jika itu tidak akurat. Saya setuju ini bukan solusi terbaik.

(Pertanyaan ini mungkin harus dipindahkan ke stack overflow (atau serupa))

@TidyIQ , jika array tidak memiliki panjang tetap, kompilator tidak dapat mengetahui apakah array tersebut cocok atau tidak.

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. 

Jadi jika array memiliki panjang dinamis, argumennya juga harus seperti itu:

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

Tempat bermain

Sayangnya itu tidak akan berhasil karena beberapa argumen saya panjangnya tetap.

Mungkin jika saya hanya memposting kode Anda mungkin melihat masalahnya.

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 , saya tidak yakin saya ikuti. Apa yang terjadi jika stateList adalah larik kosong? Dalam hal ini, apakah action.payload.value akan diberikan sebagai key ?

Seharusnya ini tidak berhasil:

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);
  }
}

Transmisikan ke nilai sebar sebagai ParameterType dari fungsi yang Anda berikan argumen.

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

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

type AddParams = Parameters<typeof add>

add(...(values) as AddParams)

@ mateja176 Ini juga tampaknya berfungsi dan mungkin lebih sesuai untuk beberapa kasus penggunaan.

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

atau

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

Saya menggunakan tipe helper untuk ini untuk menangani array arbitrer tanpa harus mengetiknya secara eksplisit:

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

Ini bisa digunakan seperti ini:

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


Penjelasan mendetail untuk pemula

Diseksi tipe NonEmptyArray menjelaskan cara kerjanya secara rinci:

# | Bagian | Penjelasan
- | - | -
(1) | type NonEmptyArray | Nama tipe helper
(2) | <T | Tipe helper mengambil parameter tipe generik T .
(3) | extends any[]> | Agar dapat diterima oleh pemeriksa tipe, parameter tipe itu T harus berupa beberapa tipe array.
(4) | T extends (infer U)[] ? | Jenis pembantu kita adalah tipe bersyarat yang memeriksa apakah T sebenarnya adalah tipe array.
Kita telah mendeklarasikan T sebagai tipe array dalam (3), jadi kondisi ini selalu lolos, tetapi menggunakan kondisional memungkinkan kita untuk membiarkan TypeScript infer tipe yang array T terbuat dari, dan kami menyebutnya jenis U .
(5) | [U, ...U[]] | Sekarang kita bisa mengumpulkan tipe yang dihasilkan: sebuah array dimana entri pertama adalah tipe U dan entri yang tersisa (0 atau lebih) juga tipe U .
Karena notasi tupel khusus ini, TypeScript menyadari bahwa setidaknya ada satu item.
(6) | : never | Ini hanya diperlukan untuk melengkapi kondisional secara sintaksis. Ingat: kondisional hanyalah trik untuk mengekstrak tipe U , dan selalu lolos. Oleh karena itu, kita dapat dengan aman mengabaikan cabang "lain" dengan menghasilkan never .


Sekarang jika kita melakukan ini ...

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

... hal berikut akan terjadi:

  • typeof values adalah jenis yang disimpulkan TypeScript untuk array values , yang merupakan array angka: number[] .
  • Itu berarti, kami memberikan number[] sebagai T . (2/3)
  • T memang merupakan tipe array, jadi, kita dapat menyimpulkan U darinya, yaitu number . (4)
  • Sekarang kita tahu U adalah number , kita menghasilkan tipe [number, ...number[]] . (5)

@tjoskar ubah kode Anda menjadi

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

( taman bermain )
Dan kesalahannya kembali.

Anehnya, jika Anda mengubah baris terakhir menjadi foo(3, ...args); - tidak akan ada kesalahan.

Saya merasa ini masih belum berhasil. Inilah contoh saya

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

Seharusnya tidak menjadi masalah apa jenis props.onSetValue , karena saya hanya mengambil jenis parameter dan meneruskannya ke fungsi tempat saya mendapatkan jenisnya dan masih memberikan kesalahan Expected 2 arguments, but got 0 or more. .

Tautan taman bermain

Berikut adalah contoh @ Haaxor1689 yang direduksi :
Tautan taman bermain

Saya masih tidak bisa membuatnya bekerja

ini adalah solusi sementara saya

class Board {
  private events: Events

  public on(...args: Parameters<this['events']['on']>) {
    this.events.on.call(this.events, ...args)
  }
}
Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

Antony-Jones picture Antony-Jones  ·  3Komentar

seanzer picture seanzer  ·  3Komentar

dlaberge picture dlaberge  ·  3Komentar

CyrusNajmabadi picture CyrusNajmabadi  ·  3Komentar

manekinekko picture manekinekko  ·  3Komentar