Как объявить общее обещание в Typescript, так что когда тип Generic "<void>", один из его методов не будет принимать параметр?
В Typescript я хочу иметь возможность определять тип Promise таким образом, чтобы я мог это сделать:
//This works today:
new Promise<number>((resolve)=>{
//Cool
resolve(5);
//Error, because I didn't pass a number:
resolve();
}
//This is what I want to do also:
new Promise<void>((resolve)=>{
//Error, because I passed a value:
resolve(5);
//Cool, because I declared the promise to be of type void, so resolve doesn't take a value:
resolve();
}
В файлах определения обещаний, которые я видел, все заявляют, что метод "решения" обещания должен принимать значение. Здесь - это недавний пример замечательного проекта DefinitelyTyped:
declare class Promise<R> implements Thenable<R> {
constructor(callback: (resolve : (result: R) => void, reject: (error: any) => void) => void);
///...
}
`` `
В основном это говорит: "Обратный вызов разрешения должен быть передан значение типа R." Это прекрасно для обещания вроде new Promise<number>
. Typescript проверит, что мы вызываем решение со значением типа number
.
Однако, если я хочу обещание, которое не имеет значения, поэтому я хочу, чтобы иметь возможность вызвать resolve() без передачи значения? Я могу объявить свое обещание следующим образом: new Promise<void>
Но тогда я все еще вынужден назвать решение и передать какую-то ценность. Я могу вызвать resolve(undefined)
, но это немного странно.
Кажется, нет способа правильно зафиксировать это понятие в Typescript: "Если этот общий тип имеет тип" void ", тогда не ожидайте параметра для этой функции."
Самое близкое, что я могу сделать, это отметить результат как необязательный в методе разрешения, но это будет означать, что результат всегда является необязательным даже для типизированных версий Promises.
Ответы
Ответ 1
Возможно, я придумал обходное решение, которым я доволен. В случае, когда я хочу иметь Promise<void>
, который гарантирует, что обратный вызов resolve
не принимает параметр, вместо того, чтобы заставить метод разрешения всегда принимать необязательный параметр, я могу определить новый класс следующим образом:
export class VoidPromise extends RSVP.Promise<void>{
//Note that resolve does not take a parameter here:
constructor(callback:(resolve:() => void, reject:(error:any) => void) => void){
super(callback);
}
}
И в этом случае я могу использовать его так:
public static testVoidPromise() : VoidPromise{
return new VoidPromise((resolve, reject)=>{
setTimeout(1000, ()=>{
if (Math.random() < 0.5){
//Note that resolve() does NOT take a parameter
resolve();
}else{
reject(new Error("Something went wrong"));
}
})
});
}
Конечно, разработчикам придется использовать VoidPromise вместо просто "Promise", но предполагаемый эффект достигается без ложной маркировки параметра разрешения как необязательного.
В моем сценарии приведенный выше код удовлетворяет моим ожиданиям больше, чем маркировка всех методов resolve
как имеющих необязательный результат. Пометить все результаты как необязательно, опасно в случае 99%. Если он всегда является необязательным, я могу объявить Promise<number>
, вызвать resolve()
без результата и получить результат undefined
для моего обещания. В таком случае я должен был отклонить это обещание. Я не верю, что ожидается, что параметр метода разрешения действительно является необязательным. (Источник: https://github.com/domenic/promises-unwrapping#the-promise-constructor)
Ответ 2
Это работает!
определения обещаний были обновлены для поддержки такого варианта использования.
Исходный пример слишком длинный для публикации. Но просто скопируйте/вставьте новые определения и код Promise в редактор, и вы увидите, что ваш пример теперь работает.
Ответ 3
По тому, как вы хотите использовать интерфейс Promise, кажется, что вы хотите иногда передавать значение, а иногда вы хотите передать никакое значение, так что для чего нужны дополнительные параметры.
Если решение с результатом "undefined" неприемлемо (IMO это не плохая идея, это говорит о том, что происходит в коде - результат обещания udefined) Я могу предложить решение, которое, похоже, является тем, что вы ищете:
Я бы определил "контракт" для обещания, скажем:
export interface IMyPromiseResult {
result: number;
}
и использовать его с обещанием:
new Promise<IMyPromiseResult>((resolve)=>{
resolve(<IMyPromiseResult>{ result: 5 });
resolve(<IMyPromiseResult>{ });
}
Хотя этот подход немного сложнее, он открывает некоторые интересные варианты...
С другой стороны, текущий конструктор DefinitelyTyped Promise определяется как:
constructor(callback: (resolve: (result?: R) => void, reject: (error: any) => void) => void);
поэтому наличие необязательного результата разрешено, и с ним все должно быть хорошо.