Typescript: Компилятор неправильно сообщает о несоответствии сигнатуры параметра / цели вызова при использовании оператора распространения

Созданный на 3 авг. 2015  ·  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 Я знаю, что вы имеете в виду, но вы можете не осознавать, что подобные утверждения контрпродуктивны. Похоже, проблема заключается в их TODO для этапов 2.7 или 2.8. Это может показаться очевидным, но когда вам нужно исправить тысячи очевидных вещей, вы не сможете исправить их все одновременно и продолжать улучшать другие вещи, которые от них ожидаются. Лучший способ озвучить свой голос - дать исходное сообщение вверху 👍 реакции. В конце концов, это бесплатное программное обеспечение, и они нам ничего не должны. Тем не менее, это всего лишь мое мнение, еще один пользователь
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. Длина массивов неизвестна во время компиляции, поэтому параметры должны быть необязательными. И аргумент о распространении должен быть последним (как сегодня).

С кортежами:

  1. В какой-то момент значение должно иметь аннотацию типа. В противном случае он будет интерпретирован как массив (например, (number | string)[] не [number, number, string] ). В игрушечном коде это сводит на нет любую экономию на краткости, но это может быть нормально в реальном проекте, который уже определяет множество интерфейсов.
  2. Длина известна заранее, поэтому кортеж можно применять к любому типу списка параметров.

Привет @sandersn!

Простите за поздний ответ. Я полностью пропустил твой ответ.
Я использую только массивы с одним типом данных, поэтому версия кортежа мне не очень интересна.
Однако я думаю, что операция распространения в вызовах функций должна принимать любой итерируемый объект. Так что он должен работать, например. map и set , и в этих случаях нам нужно будет создать интерфейс, который работает с оператором распространения, даже если данные содержат несколько типов данных (например, new Set (). Add (1) .add ('строка')). Но это может быть еще один билет?

Пример с Set и операцией распространения (работает в Chrome 46)

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

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

Ага, это отдельная тема. TypeScript уже поддерживает массивы в качестве типа для остальных параметров, и это то, что нужно сделать универсальным. Мы просто обсуждаем, где можно использовать аргументы распространения.

Вы хотите передать свои однородные массивы / наборы функциям с остальными параметрами или с фиксированным количеством аргументов? Я хотел бы получить более подробную информацию о том, по какому телефону вы собираетесь звонить.

Прежде всего, сегодня утром я поигрался с оператором распространения, и большинство моих сценариев использования отлично работают в последней версии машинописного текста [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, еще один пример реального мира - попытка глубокого слияния массива объектов с помощью слияния из lodash:

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

Поскольку это вызывает ошибку:

Поставляемые параметры не соответствуют ни одной сигнатуре цели вызова.

Даже если значение содержит правильные значения параметров конструктора Date

Даже если значение содержит правильные значения параметров конструктора Date

Это правильно, это то, что отслеживает эта проблема.

Я чувствую себя обязанным спросить, могу ли я как-то помочь / внести свой вклад. Если теперь, можете ли вы уточнить, почему именно это вызывает ошибку в данном случае? Можно как-нибудь через конфиг пропустить?

Я действительно столкнулся с этой проблемой, когда переименовал файл .js в .ts . Во всяком случае, один пример того, что «не весь действительный JS является действительным TS».

В настоящее время у меня возникла проблема с реализацией примера медиа-шаблонов styled-component.

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: тип Iterable'не является типом массива.

Случается, когда вы пытаетесь распространить итерацию ( [...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),' '));
}

На данный момент это нужно сделать.

Я открыл для этого PR на # 18004.

На данный момент мне нужно включить "noStrictGenericChecks" в моем tsconfig.

А пока мне нужно включить «noStrictGenericChecks» в моем tsconfig.

Не уверен, что понимаю, как это связано с этой ошибкой ...

Не уверен, что понимаю, как это связано с этой ошибкой.

@mhegazy Этот параметр отключает все сигнатуры функций, поэтому я могу передавать аргументы с помощью оператора распространения без сопоставления количества аргументов (по крайней мере, не статически)

это не то, что делают --noStrictGenericChecks . См. Https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#stricter -checking-for-generic-functions.

Я только что наткнулся на это.

Хотя я могу понять, что это сложно, хотя бы «лишние» аргументы надо уметь идентифицировать.

@sandersn с точки зрения реального мира, мы реорганизовали API, чтобы он принимал либо один аргумент, либо массив вместо использования аргумента rest. Таким образом, ситуации, когда мы передавали что-то вроде 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)
  }
}

Редактировать:

Чтобы прояснить путаницу, меня перенаправили отсюда , где говорится:

Ожидается 2 аргумента, но получено минимум 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 Я знаю, что вы имеете в виду, но вы можете не осознавать, что подобные утверждения контрпродуктивны. Похоже, проблема заключается в их TODO для этапов 2.7 или 2.8. Это может показаться очевидным, но когда вам нужно исправить тысячи очевидных вещей, вы не сможете исправить их все одновременно и продолжать улучшать другие вещи, которые от них ожидаются. Лучший способ озвучить свой голос - дать исходное сообщение вверху 👍 реакции. В конце концов, это бесплатное программное обеспечение, и они нам ничего не должны. Тем не менее, это всего лишь мое мнение, еще один пользователь
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, включенным в качестве аргументов, и правильной передачей 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 #). Теперь мне нужно скопировать тело свойства tableIdentification 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 - это кортеж:

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

TS-площадка

@ darekf77 , или если вы скажете, что args - это кортеж:

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 array состоит из, и мы называем этот тип U .
(5) | [U, ...U[]] | Теперь мы можем собрать результирующий тип: массив, в котором первая запись имеет тип U а остальные (0 или более) записей также имеют тип U .
Из-за этой специфической нотации кортежа 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 рейтинги