Возвращение пустого массива из функции generi c - PullRequest
3 голосов
/ 24 февраля 2020

В чем причина того, что TS не позволяет возвращать пустой массив из функции generi c, даже если этот параметр generi c расширяет (имеет ограничение) массив? Результат должен быть получен из массива.
Ниже кода:

// it does not work
async function handleErrors<Result extends Array<any>>(asyncCall: () => Promise<Result>): Promise<Result> {
  try {
    return await asyncCall();
  } catch (e) {
    return [];
    // return [] as Result; // also does not work - needed casting to `any`/`unknown` first
  }
}

Ошибка:

Тип 'never []' не может быть назначен типу 'Result'.
'never []' можно присвоить ограничению типа 'Result', но для 'Result' можно создать другой подтип ограничения 'any []'. (2322)

ИЛИ, если применяемое приведение

Преобразование типа 'never []' в тип "Result" может быть ошибкой, поскольку ни один тип не совпадает в достаточной степени с другим. Если это было сделано намеренно, сначала преобразуйте выражение в «unknown».
«never []» можно назначить ограничению типа «Result», но можно создать экземпляр «Result» с другим подтипом ограничения «any [] '. (2352)

Тип [] равен never[].

Я понимаю, что не могу вернуть массив с элементами из этой функции, поскольку точный тип будет выведен из использование (которое еще не известно, в определении функции). Это может быть массив чисел, строк и т. Д. c. поэтому я не могу вернуть, например, массив объектов. Но почему возврат пустого массива не работает?

Является ли причиной того, что возвращаемый тип может быть каким-то типом, который происходит от массива? Так что просто пустой массив не будет иметь некоторые свойства из этого типа, выведенные из использования?

Если я использую отдельный элемент в качестве параметра generi c, то все работает правильно:

async function handleErrors<Element>(asyncCall: () => Promise<Element[]>): Promise<Element[]> {
  try {
    return await asyncCall();
  } catch (e) {
    return [];
  }
}

Обе версии с примером использования: TS Playground

1 Ответ

3 голосов
/ 24 февраля 2020

Расширяя ссылку в комментарии, вот пример, почему не работает:

async function handleErrors<Result extends Array<any>>(asyncCall: () => Promise<Result>): Promise<Result> {
  try {
    return await asyncCall();
  } catch (e) {
    return [];  // <- error, this is not allowed
  }
}

// we create a type the represents an array that can't be empty and requires one string and one number
type MyArray = [string, number];
const a: MyArray = [];                    // error
const b: MyArray = ['hello world'];       // error
const c: MyArray = ['hello world', 42];   // ah, that works

// and we can properly use that with the above method. Only that the empty array from the implementation 
// above wouldn't be compatible with our type. Therefore: the error above
handleErrors<MyArray>(() => {return new Promise((resolve, reject) => {})});

Связанный ответ в комментарии объясняет то же самое, только с логическим типом, этот пример использует ваш вместо реализации.

Правка - да, но как ее решить ...?

async function handleErrors<Result extends Array<any>>(asyncCall: () => Promise<Result>): Promise<Result | []> {
  try {
    return await asyncCall();
  } catch (e) {
    return [];  // Yay!
  }
}

Вернуть обещание, которое является либо Result, либо пустым массивом.

...