microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.11k stars 12.37k forks source link

Expect to use @throws in lib/*.d.ts to mark which methods may throw err #43528

Open 2234839 opened 3 years ago

2234839 commented 3 years ago

Suggestion

Use @throws to mark the corresponding error on the method in the .d.ts file in the lib directory

šŸ” Search Terms

@throws jsdoc comments

āœ… Viability Checklist

My suggestion meets these guidelines:

ā­ Suggestion

Use @throws to mark the corresponding error on the method in the .d.ts file in the lib directory

šŸ“ƒ Motivating Example

/**
 * Converts a JavaScript Object Notation (JSON) string into an object.
 * @param text A valid JSON string.
 * @param reviver A function that transforms the results. This function is called for each member of the object.
+* @throws {SyntaxError} if the string to parse is not valid JSON. 
 * If a member contains nested objects, the nested objects are transformed before the parent object is.
 */
 parse(text: string, reviver?: (this: any, key: string, value: any) => any): any;

šŸ’» Use Cases

want to be able to write code when you know that you need to beware of these unpredictable behavior

schicks commented 3 years ago

I like this idea, but it seems much more useful if this information could be propagated by typescript, so that a function which calls something annotated with @throws and doesn't catch is also considered as throwing.

camilossantos2809 commented 3 years ago

Something like typescript-eslint/no-floating-promises would be interesting. In this rule unhandled promises shows an error on vscode.

aslilac commented 3 years ago

Throw types are the one thing I still find myself wanting in TypeScript. Sometimes the types can get a little unwieldy, but there are very few things these days that you just outright can't express in the latest TypeScript versions. The types might be complicated, but almost anything is possible. Really makes the lack of throw/catch typings feel like a pretty big hole.

function getFriend(name: string): Friend, throws ReferenceError {
    const friend = friends.get(name);
    if (!friend) throw new ReferenceError("I don't know them!");
    return friend;
}
try {
    getFriend("aslilac")
} catch (error) {
    // typeof error would be inferred as ReferenceError
}
Fleker commented 3 years ago

I manage a Typescript app that is built using Firestore and Firebase Functions, a NoSQL database and serverless app infrastructure respectively. Firestore provides a pretty handy async API for putting stuff in a database.

async function putStuff(theStuff: any) {
     // Optionally do data validation here
     await firestore.collection(...).doc(...).set( theStuff );
}

I then wrap this stuff in a Cloud Function.

export my_function = functions.https.onCall(async (data, context) => {
    await putStuff(data);
    return 'Stuff has been put.';
})

The Firestore function call may throw an error in some circumstances, which then causes putStuff to fail. Following that, my Cloud Function throws an error back to the client. Though my client would ordinarily display a useful note, Cloud Function would generate a '500 INTERNAL' error as a default to the user which is not very useful.

My issue is not pertaining to Cloud Functions itself, as the better solution is to use a try/catch block on my async function and handling that in a user-friendly manner.

export my_function = functions.https.onCall(async (data, context) => {
    try {
        await putStuff(data);
    } catch (e) {
        return `Sorry, we cannot put the stuff. Error ${e}`;
    }
    return 'Stuff has been put.';
})

I do think ensuring that a function has a try/catch block is something that should be enforced at the compiler, under a particular optional flag, so that it signals to particular developers that there is code that should be handled before your execution returns to the framework. Particularly in a Node ecosystem moreso than other languages, the employment of many frameworks means that a higher-level solution would not be as scalable.

iulianraduat commented 3 years ago

@throws looks like a Decorator So I would like to see something more like a type, for example: Throws In this way intellisense will help me know that a function can also throw an error. (This is my use case for wanting to have this type; so I know that I need to wrap it in a try catche.) Currently typescript provides the type never, which also is an indication that the function will throw an error (or maybe has an infinite loop?). Maybe should never type start being generic so we also can indicate the type of thrown error?

fwienber commented 1 year ago

Are you aware of TSDoc and its definition of the TSDoc @throws tag? Better support and tooling for TSDoc (e.g. eslint TypeScript rules that consider TSDoc tags as defined in the standard) could resolve or at least mitigate this problem.