TypeScript-Handbook
TypeScript-Handbook copied to clipboard
please add typing example for function overloading callback or Promise
I cannot figure out how to write types for a common pattern I use. I have an async function, that optionally takes a callback, and:
- calls the callback if one is provided, returning nothing
- returns a Promise, if there is no callback
Can you please update the handbook to provide an example of the correct way to do this?
My overloaded type declarations are:
doSomethingAsync<T>(done: (error: Error, result: T) => void): void
doSomethingAsync<T>(): Promise<T>
The function definition is:
// desired return type: Promise<T> | void {
doSomethingAsync<T>(done?: (error: Error, result: T) => void): any {
if (done) {
something_via_callback(done)
} else {
return something_via_promise()
}
}
Note that I'm using any, which just avoids the type system. But when I use Promise<T> | void I get errors. I cannot figure out how to type this.
Please see this question I asked on StackOverflow, with a link to code on the TypeScript Playground: http://stackoverflow.com/questions/40138730
Also, it seems that I had this code working previously, but I can no longer find a version that did work.
This seems to work right now. Any clue if you meant something else?
I think even when you use any
for the return type or do not write any return type at all for the function implementation, when using the function the correct return type can still be inferred with a given parameter, so you are not "avoid the type system" here. And technically Promise<T> | void
is not the correct return type, because it does not provide much safety. It allows Promise<T>
in places only void
can be allowed for example.
@DanielRosenwasser
Unfortunately, this still persists for me. I'm on a Mac with OSX 10.11.6, and have been using several recent versions of TypeScript.
I see that the code from your link to the Playground doesn't show the error, but I also noticed that the first two lines of the class are no longer comments in that version. Clicking on the example in the StackOverflow issue still shows the error, and I have a copy of the simplified failing code below.
Steps to Reproduce using the example code I created a file, t.ts, with this content:
/// <reference path="typings/globals/es6-shim/index.d.ts" />
declare abstract class SlimDocumentDatabase<T> {
create(obj: T): Promise<T>
create(obj: T, done: (error: Error, result?: T) => void): void
}
class SlimAdaptor<DocumentType> implements SlimDocumentDatabase<DocumentType> {
create(obj: DocumentType, done?: (error: Error, result?: DocumentType) => void) : void | Promise<DocumentType> {
if (done) {
done(undefined, obj);
} else {
return Promise.resolve<DocumentType>(obj)
}
}
}
Then I built it with tsc --module commonjs t.ts, but the typescript compiler outputs this error:
t.ts(9,7): error TS2420: Class 'SlimAdaptor<DocumentType>' incorrectly implements interface 'SlimDocumentDatabase<DocumentType>'.
Types of property 'create' are incompatible.
Type '(obj: DocumentType, done?: (error: Error, result?: DocumentType) => void) => void | Promise<Docum...' is not assignable to type '{ (obj: DocumentType): Promise<DocumentType>; (obj: DocumentType, done: (error: Error, result?: D...'.
Type 'void | Promise<DocumentType>' is not assignable to type 'Promise<DocumentType>'.
Type 'void' is not assignable to type 'Promise<DocumentType>'.
I've been unable to decipher this error message.
Steps to Reproduce using one of my projects
Here is one of my projects that exhibits the problem: https://github.com/psnider/in-memory-db I just updated the dependencies, so it now uses TypeScript 2.0.6 (I believe I've previously tested with 2.0.0, 2.0.2, and 2.0.3)
I just cloned in-memory-db into a new directory, ran npm install, confirmed that the typescript compiler is v2.0.6, and built the code using npm run build. It compiles cleanly, because my implementing functions are typed as returning any.
git clone [email protected]:psnider/in-memory-db.git
cd in-memory-db
tsc -v
// shows: Version 2.0.6
which tsc
// shows: node_modules/.bin/tsc
npm run build
// builds cleanly
After I edit line 139 to be:
create(obj: DataType, done?: ObjectCallback<DataType>): Promise<DataType> | void {
the typescript compiler outputs an error similar to the one above.
For reference, the create function in DocumentDatabase is declared as:
export abstract class DocumentDatabase<DocumentType extends DocumentBase> {
create(obj: DocumentType): Promise<DocumentType>
create(obj: DocumentType, done: ObjectCallback<DocumentType>): void
}
and in-memory-db.d.ts extends DocumentDatabase.
@zhengbli
I think even when you use any for the return type or do not write any return type at all for the function implementation, when using the function the correct return type can still be inferred with a given parameter, so you are not "avoid the type system" here.
I think you're referring to compilation context for the caller of the function. In that case, I agree, because the declaration file has the correct types.
But I'm referring to the implementation. In which case, using any or no type provides no assistance if I implement the function incorrectly. And I implement functions incorrectly all the time! I find the TypeScript feedback invaluable for when I make implementation mistakes, so I want it as tight as possible.
And technically Promise<T> | void is not the correct return type, because it does not provide much safety.
I don't understand this comment. I believe Promise<T> | void is the correct type, as there are two execution paths, each returning a different type. You say it doesn't provide much safety, but I don't know how to type this any more strictly. Can you please provide an example of how to do this?
@psnider you need to include the original overloads because otherwise you lose them in the subclass. When the subclass's signature for create
is checked for assignability with each overload in the superclass, it will fail because Promise<T> | void
is not assignable to Promise<T>
nor void
individually.
Somehow I missed that I can put declarations within the class! I've been using TypeScript since v0.8, and I don't think I've come across this. Thanks.
I looked for the documentation that suggests that I can add declarations to the class, and finally found it at:
- https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#83-constructor-declarations
- https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#61-function-declarations
I still think it would be helpful to add an example such as this to the documentation.
How to write the definition for a Promise function when it rejects an Error?