-
-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: improve function return types #60
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
): Promise<
| {
data: { message: string }
error: null
}
| {
data: null
error: Error // in future we might add Error || OurOwnError
}
> {
const data = something
return { data, error: null }
} catch (error) {
if(basicErrorTypeguard(error)) return { data: null, error }
// In case we would need to add new Error type in future.
//if(ourOwnErrorTypeguard(error)) return { data: null, error }
throw error
}
}
Reasoning:
- If there will be any other error that is not handled by us, we shouldn't return it without a type.
- User won't be able to handle it, returning an unknown type as error isn't an option as well as that makes developer experience completely worthless. We might as well return a flag 'isError'
Why this solution:
Typeguard will ensure that we only ever return our 'Error' and users can handle it.
They will get an error in development that will give them information.
Bonus:
Backward compatibility.
With this code it's possible to add new ErrorTypes in future without breaking anything. Its maintanable
Another possible solution (not recommended):
catch (error) {
const castedError = error as Error
return { data: null, error }
}
This will also work, but it will tell the developer that the error is of type 'Error'.
If any other error is thrown, the developer won't get information about it. This is bug prone.
For example:
- Developer shows information from the error to user (user of website).
- Any other error is thrown that doesn't match 'Error'.
- Javascript might crash because of accessing a property that doesn't exist.
Thanks, @Jaaneek! I agree with your first option more than the second. The only thing I don't feel great about is returning an error in the object, but having the function also be able to throw... If a user sees that error in the object, my intuition is that all errors would come through that error and not through a try/catch. I'm keen to hear your thoughts! |
My proposal: Create custom error that will format any unknown error try {
throw new Error('Some supabase error')
} catch (error) {
if (error instanceof Error) return error
// if(error instaceof SomeOtherError) return error
// if(error instaceof AnotherError) return error
return toStorageError(error,Reason.Unknown)
// Might be: return toStorageError(error,Reason.UpdateFail)
} const toStorageError = (error:unknown,reason:Reason) => {
if(isErrorWithMessage) return new StorageError(error.message,reason,error)
// We might aswell do:
// return new StorageError('',reason,error)
try {
return new StorageError(JSON.stringify(maybeError),reason,error)
} catch {
// fallback in case there's an error stringifying the maybeError
// like with circular references for example.
return new StorageError(String(maybeError),reason,error)
}
} export class StorageError extends Error {
reason: Reason
originalError:unknown
constructor(message: string, reason: Reason,originalError:unknown) {
super(message)
this.name = 'StorageError'
this.Reason= status
this.,originalError: originalError
}
}
}
enum Reason {
Unknown,
OtherEasons...
} function isErrorWithMessage(error: unknown): error is ErrorWithMessage {
return (
typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof (error as Record<string, unknown>).message === 'string'
)
} Why?
Suggestions More to read about this pattern |
@mirceanton, I'll review your PRs now and try and get them in before this goes out. Thank you! |
🎉 This PR is included in version 1.8.0-next.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
🎉 This PR is included in version 2.0.0-rc.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
* feat: improve function return types (#60) * feat: improve types * chore: better error handling * feat: custom storage api error * chore: use custom error type * chore: update custom error type * chore: replace instanceof with isStorageError * chore: move isStorageError to a non-static method * Improve `uploadOrUpdate` returned data. (#63) * fix: typo in updateBucket jsdoc * fix: import cross-fetch conditionally * fix: es2020 * Also return the clean path for the uploaded file. * Update readme. * Add bucket id in returned data. * Update readme * Fix return type for `upload` and `update`. * Fix typo in code snippet comment. * Rename `downloadPath` to `path` * Update return types Co-authored-by: Jonathan Picques <jonathan.picques@gmail.com> Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev> Co-authored-by: Mircea-Pavel Anton <mircea.anton99@yahoo.com> * Improve `getPublicUrl` (#62) * Improve `getPublicUrl` * Make return types consistent across all functions. * Undo return type change * Change the `getPublicUrl` method to return only the url as string. * Remove redundant method. * Update tests. Co-authored-by: Mircea-Pavel Anton <mircea.anton99@yahoo.com> * upload, update: don't return bucketId as a return parameter and in the path. Just returning the path without the bucket id makes it easier to pass the value in to other storage-js functions. Also not calling it Key -- was uppercase and not as clear as just calling it path. * add docs folder to .gitignore * Merge branch 'master' into chore/merge-master-04-08-22 (#82) * fix: remove release config from package.json (#83) * chore: merge main into next (#85) * fix: typo in updateBucket jsdoc * fix: import cross-fetch conditionally * fix: es2020 * chore: add search param to SearchOptions jsdoc on list function (#59) * build(release-next): sets up the next branch as an npm prerelease (#80) * fix: rename main release branch (#84) Co-authored-by: Jonathan Picques <jonathan.picques@gmail.com> Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev> * fix: encode all urls fixes #78 this should ideally be done in the api server, but doing this breaking change at the client library first, so when we do it at the backend, only folks who are using the api directly will need to upgrade. * return values is always wrapped by data signedURL used to return a url directly and inside the data object. This is inconsistent. Now we always return values inside a data object only. * fix: pass through all values returned by backend instead of cherry picking only name - we still do this in the types though. also ensures data is always an object. Fixes #6 * fix tests * fix metadata type Previously accessing metada.contentType would throw a type error since we set the type to an empty object. * only export StorageClient #46 removing the change we added for backward compatibility * update ci to node 16 * upgrade typedoc to latest version * fix: move bucket and file api to package folder docs look better when we do this and add the packages to the entrypoint to the typedoc command * modify publicurl to always return data all methods return data and an error * dont return error for getPublicUrl * fix: signed url is returned as signedUrl matches the method name createSignedUrl * downgrade typedoc to 0.22.16 our doc generation pipeline only works with 0.22.16 for now * exclude protected properties from typedoc * fix: typedocs * chore: merge main into next (#99) * fix: typo in updateBucket jsdoc * fix: import cross-fetch conditionally * fix: es2020 * chore: add search param to SearchOptions jsdoc on list function (#59) * build(release-next): sets up the next branch as an npm prerelease (#80) * fix: rename main release branch (#84) * build(release-rc): sets up the rc branch as an npm prerelease (#98) Co-authored-by: Jonathan Picques <jonathan.picques@gmail.com> Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev> * feat: Release V2 RC BREAKING CHANGE: Release V2 * update docs (#102) * Fix docs typo (#104) * chore: increased test coverage (#106) * fix: consistent return types for copy (#110) * feat: download file via url (#112) * fix: merge main into rc (#113) * fix: typo in updateBucket jsdoc * fix: import cross-fetch conditionally * fix: es2020 * chore: add search param to SearchOptions jsdoc on list function (#59) * build(release-next): sets up the next branch as an npm prerelease (#80) * fix: rename main release branch (#84) * build(release-rc): sets up the rc branch as an npm prerelease (#98) * Adds v1 docs (#107) * docs: ci Co-authored-by: Jonathan Picques <jonathan.picques@gmail.com> Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev> Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com> Co-authored-by: Mircea-Pavel Anton <contact@mirceanton.com> Co-authored-by: Jonathan Picques <jonathan.picques@gmail.com> Co-authored-by: Bobbie Soedirgo <bobbie@soedirgo.dev> Co-authored-by: Mircea-Pavel Anton <mircea.anton99@yahoo.com> Co-authored-by: Inian <inian1234@gmail.com> Co-authored-by: Fabrizio <fabri.feno@gmail.com> Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com>
Fixes #55.
What is the current behaviour?
There are some type errors, and functions return everything with
| null
, making it difficult for end-users to work with.What is the new behaviour?
Functions now return type unions, which are more correct and easier to work with.
Additional context
I've labelled this as
feat
instead ofchore
as it should be treated as a breaking change because it may show new type errors to existing users.