Typescript: يقوم المترجم بالإبلاغ بشكل غير صحيح عن عدم تطابق توقيع المعامل / استدعاء الهدف عند استخدام عامل الانتشار

تم إنشاؤها على ٣ أغسطس ٢٠١٥  ·  55تعليقات  ·  مصدر: microsoft/TypeScript

أبلغ المحول البرمجي بشكل غير صحيح عن عدم تطابق توقيع الهدف / الاستدعاء عند استخدام عامل الانتشار. لصق هذا الرمز في TypeScript Playground:

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

ينتج هذا الخطأ:

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

التعليق الأكثر فائدة

@ jpike88 أعلم أنك تقصد جيدًا ، لكنك قد لا تدرك أن عبارات مثل هذه تأتي
من TS مثلك 🕺

ال 55 كومينتر

وينطبق الشيء نفسه عندما يكون args من النوع any

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

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

هذا حاليا حسب التصميم. لكن يجب أن نعيد النظر فيه.

smashdevcode ، tjoskar ، أبحث عن بعض الاستخدامات الواقعية لهذه الميزة. على وجه التحديد ، هل تتوقع نشر المصفوفات أو المجموعات (أو ربما كلاهما)؟ إليك بعض الأمثلة على الألعاب:

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

الآن يمكن تطبيق أي من a1,a2,a3 على أول وظيفتين ، وأي من t1,t2 يمكن تطبيقه على الوظيفتين الثانية.

لاحظ ذلك باستخدام المصفوفات:

  1. لديك نوع واحد ، لذلك يجب أن تكون جميع المعلمات من نفس النوع. (أعتقد أن هذا قد يكون any .)
  2. لا يُعرف طول المصفوفات في وقت الترجمة ، لذا يجب أن تكون المعلمات اختيارية. ويجب أن تكون حجة الانتشار هي الأخيرة (مثل اليوم).

مع tuples:

  1. يجب أن تحتوي القيمة على نوع التعليق التوضيحي في مرحلة ما. وإلا سيتم تفسيرها على أنها مصفوفة (على سبيل المثال ، (number | string)[] وليس [number, number, string] ). في رمز اللعبة ، يؤدي هذا إلى محو أي مدخرات في الإيجاز ، ولكن قد يكون الأمر جيدًا في مشروع حقيقي يحدد بالفعل الكثير من الواجهات.
  2. الطول معروف مسبقًا ، لذلك يمكن تطبيق tuple على أي نوع من قائمة المعلمات.

مرحبًا sandersn ،

عذرا على الرد المتأخر. لقد فاتني ردك تمامًا.
أنا أستخدم المصفوفات بنوع بيانات واحد فقط ، لذا فإن نسخة tuple ليست ممتعة جدًا بالنسبة لي.
ومع ذلك ، أعتقد أن عملية الانتشار في استدعاءات الوظائف يجب أن تقبل أي كائن قابل للتكرار. لذلك يجب أن تعمل مع على سبيل المثال. map و set ، وفي هذه الحالات سنحتاج إلى إنشاء واجهة تعمل مع عامل الانتشار حتى إذا كانت البيانات تحتوي على أنواع بيانات متعددة (على سبيل المثال ، مجموعة جديدة (). إضافة (1) .add ("سلسلة")). ولكن ربما هذه تذكرة أخرى؟

مثال مع Set وعملية الانتشار (تعمل في Chrome 46)

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

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

نعم ، إنها قضية منفصلة. تدعم الكتابة النصية بالفعل المصفوفات كنوع لمعلمات الراحة ، وهو الشيء الذي يجب أن يكون عامًا. نحن فقط نناقش أين يمكن استخدام فرق الأسعار.

هل تريد أن تمرر المصفوفات / المجموعات المتجانسة إلى دوال ذات معلمات الراحة أو تلك التي تحتوي على عدد ثابت من الوسائط؟ أود المزيد من التفاصيل حول الشيء الذي قد تتصل به.

بادئ ذي بدء ، لقد لعبت مع عامل الانتشار هذا الصباح ومعظم حالات الاستخدام الخاصة بي تعمل بشكل جيد في أحدث إصدار من الكتابة المطبوعة [email protected] وعلى http://www.typescriptlang.org/Playground. إذن هذه ليست مشكلة كبيرة بعد الآن.

ومع ذلك،
لا أحب فكرة جعل جميع المعلمات اختيارية لمجرد التمكن من استخدامها
انتشار المشغل لكنني أفهم المشكلة ؛ طول المصفوفات غير معروف في وقت الترجمة بذلك
من المستحيل التحقق من المعلمات في وقت الترجمة.

ومع ذلك ، أعتقد أنه سيكون من الجيد ألا تكون المعلمات اختيارية وأن المترجم
تحقق فقط من أن "متغير الانتشار" من النوع الصحيح على سبيل المثال. رقم[]

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

هل هذا ممكن؟

تعمل التعليمات البرمجية التالية في الإصدار الحالي من الكتابة المطبوعة ، والتي لم تكن تعمل عند معالجة هذه المشكلة.

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

ربما يكون مثالًا أكثر واقعية (يعمل أيضًا في الوقت الحاضر):

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: مواجهة نفس المشكلة ، مع حالة الاستخدام المحددة للمنشئ Date :

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

بالنسبة لنا ، نستخدم الحيزات للسماح للمستخدمين بتكوين متطلبات argv في تطبيق أساسي دون الحاجة إلى بناء مساعدين خاصين بهم. فمثلا:

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

مثال من العالم الحقيقي باستخدام وظيفة combLatest من https://github.com/ReactiveX/rxjs حيث أريد الحفاظ على this عند استدعاء projectFn باستخدام دالة السهم واستخدام معلمات الباقي نشر الحجج.

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

حاليا علي أن أفعل

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

FWIW ، مثال آخر من العالم الحقيقي يحاول دمج مجموعة من الكائنات بعمق باستخدام الدمج من اللوداش:

import { merge } from 'lodash';

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

إليك حالة استخدام أخرى في العالم الحقيقي ؛ نحن نحاول إعادة صياغة كود يشبه هذا:

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

في الشكل الأكثر أناقة:

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

لكن استخدام عامل الانتشار يؤدي إلى حدوث خطأ عدم تطابق التوقيع.

مثال آخر

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

    }
}

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

TS2346: لا تتطابق المعلمات المتوفرة مع أي توقيع لهدف الاستدعاء.

نفس المشكلة مع طرق التحميل الزائد:

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;

بينما دعا

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

ثم: _ [ts] المعلمات الموفرة لا تطابق أي توقيع لهدف الاستدعاء.

حتى الآن من المستحيل القيام بهذا الشيء:

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

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

لأنها تنتج الخطأ:

لا تتطابق المعلمات المتوفرة مع أي توقيع لهدف الاستدعاء.

حتى إذا كانت القيمة تحتوي على قيم معلمات مُنشئ التاريخ الصحيحة

حتى إذا كانت القيمة تحتوي على قيم معلمات مُنشئ التاريخ الصحيحة

هذا صحيح ، هذا ما تتبعه هذه القضية.

أشعر بأنني مضطر للسؤال عما إذا كان بإمكاني المساعدة / المساهمة بطريقة ما. إذا كنت الآن ، هل يمكنك توضيح سبب التسبب في حدوث خطأ في هذه الحالة بالضبط؟ هل يمكن تخطيها بطريقة ما من خلال التكوين؟

لقد واجهت هذه المشكلة بالفعل عندما أعدت تسمية ملف .js إلى .ts . على أي حال ، أحد الأمثلة على "ليست كل JS صالحة هي TS صالحة".

أواجه حاليًا هذه المشكلة في تنفيذ مثال قوالب وسائط المكون المصمم.

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

الحل البديل الخاص بي هو استخدام css.call ، والذي يعمل على الأقل مع any[] النحو التالي:

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

نفس المشكلات ، المثال الخاص بي هو وظيفة الدمج الأساسية:

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

هل هناك أي حل حول كيفية كتم هذا الخطأ على الأقل لكل ملف؟

في الواقع ، أود أن أرى أسباب تعطل احتمال ES6 هذا حسب التصميم أو أي خطط عند إعادة النظر فيه. mhegazy الرجاء التعليق.

لقد أجرينا مؤخرًا تغييرًا للسماح بالانتشار في تعبيرات استدعاء إذا كان الهدف اختياريًا (راجع https://github.com/Microsoft/TypeScript/pull/15849). يعمل التغيير من خلال معالجة ناتج الانتشار كمجموعة لا نهائية من الوسائط الاختيارية.

مع هذا التغيير ، إليك الحالة الحالية في الأمثلة:

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

في المثال الأخير ، ليس لدى المحول البرمجي أي طريقة للتحقق من أن args يلبي baz المطلوب ، نظرًا لأن طول args لا يمكن التحقق منه بشكل ثابت.

ما تبقى في هذا المجال هو السماح للجولات ذات الحجم المعروف بأن تفي بالوظائف ذات الوسيطات المطلوبة بنفس الطول .. ولكن هذا ليس بالبساطة التي قد يبدو عليها. على سبيل المثال:

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

لدي نفس المشكلة مثل devrelm

ما زلت أواجه مشكلة مع هذا ، فما هو الخطأ؟

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

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

يعطي خطأ في الملعب المطبوع

owlcode هذا في TS 2.4 لكن الملعب يعمل على TS 2.3 حتى بعد إصدارات 2.4.

يبدو أن هذا تم إصلاحه في TS 2.4 ،

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

ومع ذلك ، يبدو أن هناك خطأ جديدًا في TS 2.4:

TS2461: اكتب "قابل للتكرارليس نوع مصفوفة.

يحدث عندما تحاول نشر متكرر ( [...obj] ).

ومع ذلك ، هناك خطأ مختلف (على ما أظن) في 2.4.1-insiders.20170615 لم أتمكن من اكتشافه بعد.

لا أعرف كيف أتغلب على هذا أيضًا. لن يساعد إرسال المصفوفات إلى any[] .


مهما يكن ، يمكننا إصلاح تعريف 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;
}

ثم صب الكائن console :

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

هذا ما يجب القيام به الآن.

فتحت العلاقات العامة لهذا في # 18004.

في الوقت الحالي ، سيتعين علي تمكين "noStrictGenericChecks" في tsconfig الخاص بي.

في الوقت الحالي ، سأضطر إلى تمكين "noStrictGenericChecks" في tsconfig الخاص بي.

لست متأكدًا من أنني أرى كيف يرتبط هذا بهذا الخطأ ...

لست متأكدًا من أنني أرى كيف يرتبط هذا بهذا الخطأ.

mhegazy يعطل هذا الإعداد جميع تواقيع الوظائف ، لذا يمكنني تمرير الوسائط باستخدام عامل الانتشار دون مطابقة عدد الوسائط (على الأقل ليس بشكل ثابت)

هذا ليس ما يفعله --noStrictGenericChecks . يرجى مراجعة https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#stricter -checking-for-generic-function

لقد جريت عبر هذا للتو.

بينما أستطيع أن أفهم أنه أمر صعب ، يجب على الأقل تحديد الحجج "الزائدة".

sandersn من منظور العالم الحقيقي ، كنا نعيد بناء واجهة برمجة تطبيقات إما لقبول وسيطة واحدة أو مصفوفة ، بدلاً من استخدام وسيطة الراحة. لذا المواقف التي مررنا فيها شيئًا مثل foo(value, ...otherValues) لم يتم تحديدها على أنها أخطاء. يبدو هذا على وجه التحديد خطيرًا حقًا :

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

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

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

kitsonk هل يمكنك فتح خطأ منفصل لذلك؟ قد يكون من الأسهل التفكير في العزلة ، وربما يكون تعديلًا صغيرًا للقواعد الحالية ، وليس تغييرًا معقدًا.

حتى لو تحققت من الطول ، ما زلت أتلقى هذا الخطأ:

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

تعديل:

لتوضيح الالتباس ، تمت إعادة توجيهي من هنا ، والتي تنص على:

من المتوقع وجود وسيطتين ، ولكن تم الحصول على 0 على الأقل

تم اعتبار هذه المشكلة تكرارًا لهذه المشكلة.

في حالتي ، تمكنت من إصلاح خطأي (مثل الاختراق أكثر من الإصلاح) من خلال السماح بعدم تمرير أي شيء إلى الطريقة عن طريق إضافة function otherFunc() كأحد المُبدِعين مثل ذلك:

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

يصبح هذا الحل معطلاً في 2.7

كيف لا يزال هذا مشكلة؟ تعد عوامل فروق الأسعار طريقة متوافقة تمامًا للعمل في JS ، لذلك يجب على TS حسابها بشكل صحيح.

@ jpike88 أعلم أنك تقصد جيدًا ، لكنك قد لا تدرك أن عبارات مثل هذه تأتي
من TS مثلك 🕺

حالة اختبار أخرى لطيفة (في الواقع انحدار لـ TS 2.7) هي:

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

وتنتج

test.ts (20،9): خطأ TS2557: من المتوقع وجود 0 وسيطة على الأقل ، ولكن تم الحصول على 0 أو أكثر.

مشكلة أخرى صادفتها بخصوص عامل الانتشار:

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'

بالطبع ، في ES6 ليس لدي مشكلة في نشر المصفوفة مع تضمين غير محدد كوسيطات وتمرير undefined بشكل صحيح ولكن يبدو أن TS تجاهل قيم undefined عندما نشرها.

مثال آخر من العالم الحقيقي ، حيث سيساعد التعامل الصحيح مع عامل الانتشار في استدعاء الوظيفة كثيرًا:
لدي وظائف تم إنشاؤها بواسطة NSwagStudio - مُنشئ API لـ C # WebApi ، والتي لها نفس المعلمات (معلمات GET محددة في الهيكل)

تبدو الوظيفة التي تم إنشاؤها كما يلي:

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

أريد أن أسميها مع بناء الجملة

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

لأن هناك المزيد من الوظائف مع نفس المعلمات بالضبط (لأنها تنشأ من نفس فئة المعلمات المحددة في C # الخلفية). الآن لا بد لي من نسخ نص جدول الخاصية n- مرات في كل استخدام

smashdevcode بالنسبة لي كان الحل هو إضافة @ ts-ignore

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

مثال هنا: https://stackblitz.com/edit/typescript-ecymei؟embed=1&file=index.ts

@ darekf77 ، أو إذا قلت أن args عبارة عن مجموعة tuple:

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

ملعب ts

@ darekf77 ، أو إذا قلت أن args عبارة عن مجموعة tuple:

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

يعمل هذا بالنسبة لي عندما أقوم بترجمة الكتابة المطبوعة في سطر الأوامر ، ولكن ليس عندما يتحقق Visual Studio Code من النص المكتوب أثناء الكتابة. لست متأكدا ما هو قطع الاتصال هنا.

أرى أنه قد مر أكثر من عام منذ أن ذكر jayphelps أنه سيتم إصلاح هذا في إصدار قادم ، لكن هذه المشكلة لا تزال تحدث. هل هناك أي تحديث على ذلك؟ أعلم أنه يمكنني استخدام @ ts-ignore ، لكن هذا يزيل جميع الرسائل المطبوعة للوظيفة ، لذا فهو ليس حلاً مناسبًا حقًا.

TidyIQ كما هو مذكور من قبل tjoskar ، قم بتلبيس مصفوفة السبريد الخاصة بك على هيئة مجموعة

المصفوفة ليست ذات طول ثابت لذا لن يعمل ذلك.

ستعمل أداة التلبيس بقدر ما يتعلق الأمر بالمترجم ، حتى لو كانت غير دقيقة. أوافق على أنه ليس الحل الأفضل.

(ربما ينبغي نقل هذا السؤال إلى تجاوز سعة المكدس (أو ما شابه))

TidyIQ ، إذا لم يكن للمصفوفة طول فلن يتمكن المحول البرمجي من معرفة ما إذا كانت المصفوفة مناسبة أم لا.

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. 

لذلك إذا كانت المصفوفة ذات طول ديناميكي ، فيجب أن تكون الوسائط كذلك:

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

ملعب

لسوء الحظ ، لن ينجح ذلك لأن بعض حجراتي ثابتة الطول.

ربما إذا قمت بنشر الرمز للتو ، فقد ترى المشكلة.

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 ، لست متأكدًا من متابعي. ماذا يحدث إذا كان stateList مصفوفة فارغة؟ في هذه الحالة ، سيتم تمرير action.payload.value على أنه key ؟

ألا يجب أن يعمل هذا:

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

تم إرساله لتوزيع القيمة على أنها ParameterType للدالة التي تقوم بتمرير الوسائط إليها.

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

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

type AddParams = Parameters<typeof add>

add(...(values) as AddParams)

@ mateja176 يبدو أن هذا أيضًا يعمل وقد يناسب بعض حالات الاستخدام بشكل أفضل.

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

أو

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

أستخدم نوعًا مساعدًا لهذا للتعامل مع المصفوفات العشوائية دون الحاجة إلى كتابتها صراحة:

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

يمكن استخدامه على النحو التالي:

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


شرح مفصل للمبتدئين

يوضح التشريح التالي للنوع NonEmptyArray كيفية عمله بالتفصيل:

# | الجزء | تفسير
- | - | -
(1) | type NonEmptyArray | اسم نوع المساعد
(2) | <T | يأخذ نوع المساعد معلمة نوع عامة T .
(3) | extends any[]> | لكي يتم قبولها بواسطة مدقق النوع ، يجب أن تكون معلمة النوع T من نوع مصفوفة.
(4) | T extends (infer U)[] ? | نوع المساعد الخاص بنا هو نوع شرطي يتحقق مما إذا كان T هو نوع مصفوفة بالفعل.
لقد أعلنا عن T كنوع مصفوفة في (3) ، لذلك يمر هذا الشرط دائمًا ، لكن استخدام الشرط يسمح لنا بالسماح لـ TypeScript infer بالنوع الذي يحتوي عليه مصفوفة T مصنوع من ، ونطلق عليه هذا النوع U .
(5) | [U, ...U[]] | الآن يمكننا تجميع النوع الناتج: مصفوفة يكون الإدخال الأول فيها من النوع U والإدخالات المتبقية (0 أو أكثر) هي أيضًا من النوع U .
بسبب ترميز tuple المحدد ، تدرك TypeScript أن هناك عنصرًا واحدًا على الأقل.
(6) | : never | هذا مطلوب فقط لإكمال الشرطي نحويًا. تذكر: الشرط هو مجرد خدعة لاستخراج النوع U ، وهو دائمًا ما يمر. لذلك ، يمكننا تجاهل فرع "else" بأمان من خلال تحقيق never .


الآن إذا فعلنا هذا ...

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

... سيحدث ما يلي:

  • typeof values هو النوع الذي استنتجته TypeScript لمصفوفة values ، وهي مجموعة من الأرقام: number[] .
  • هذا يعني أننا مررنا number[] كـ T . (2/3)
  • T هو نوع مصفوفة بالفعل ، لذا يمكننا استنتاج U منه ، وهو number . (4)
  • الآن بعد أن علمنا أن U هو number ، فإننا ننتج النوع [number, ...number[]] . (5)

tjoskar قم بتغيير الكود الخاص بك إلى

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

( ملعب )
والخطأ عاد.

والمثير للدهشة أنك إذا قمت بتغيير السطر الأخير إلى foo(3, ...args); - فلن يكون هناك خطأ.

أشعر أن هذا ما زال لا يعمل. هنا هو مثالي

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

لا يهم حقًا ما هو نوع props.onSetValue ، لأنني فقط آخذ نوع المعلمات وقمت بتمريره إلى الوظيفة التي حصلت على النوع منها ولا يزال يعطي الخطأ Expected 2 arguments, but got 0 or more. .

رابط الملعب

فيما يلي نموذج مختزل لمثال @ Haaxor1689 :
رابط الملعب

ما زلت لا أستطيع تشغيله

هذا هو الحل المؤقت الخاص بي

class Board {
  private events: Events

  public on(...args: Parameters<this['events']['on']>) {
    this.events.on.call(this.events, ...args)
  }
}
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات