-
Notifications
You must be signed in to change notification settings - Fork 8
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
Export DatabaseError, ErrorLevel, and TransactionStatus as values #120
Comments
I do question doing this for This should be an integer comparison where the symbol |
Turns out some tools (which I use) don't play well with This comment was helpful for my understanding. Essentially, TypeScript compiler looks at enums in
Hence when running this test in vitest, the TS code is compiled to roughly this JS code: import { TransactionStatus, connect } from "ts-postgres";
import { expect, test } from "vitest";
test("Initially, transactionStatus is Idle", async () => {
const client = await connect({
database: "postgres"
});
expect(client.transactionStatus).toBe(TransactionStatus.Idle);
}); Where
When exporting as valueI've created a repository with two commits: one uses Here's the repository: https://github.com/azerum/ts-postgres-const-enum You can see the README.md for results (tsc/esbuild output, esbuild runtime behavior, vitest output) In short, when exporting TransactionStatus as value, TypeScript generates this code in export var TransactionStatus;
(function (TransactionStatus) {
TransactionStatus[TransactionStatus["Idle"] = 73] = "Idle";
TransactionStatus[TransactionStatus["InTransaction"] = 84] = "InTransaction";
TransactionStatus[TransactionStatus["InError"] = 69] = "InError";
})(TransactionStatus || (TransactionStatus = {})); This happens because you have This allows vitest test to pass and esbuild-built code to run. Yet, when the user of the library compiles code with regular tsc, inlining is still applied (see The only disadvantage of const-enum + preserveConstEnums I see is increased emitted code size. I thing it's a better trade-off to support widely used |
The change to I think the solution is to drop the use of enums as a public interface since they're inefficient without their It would seem that a simple constant assignment such as type TransactionStatus = TransactionStatusIdle | TransactionStatusInTransaction | ... A stricter solution would be the use of symbols I suppose, but this seems unnecessary to me. Thanks for taking the time to explain this behavior in such detail. |
Thanks for sharing the idea I don't use enums often, and the issue #100 does not specify concrete examples of when enums are inefficient. Are they problematic because of the increased emitted code size? Or because they compile to functions that construct an object with potentially many keys? Have enums ever caused issues in practice? The solution with constants would be along a lines of this? export const TransactionStatusIdle = 0x49
export const TransactionStatusInTransaction = 0x54
export const TransactionStatusInError = 0x45
export type TransactionStatus =
| typeof TransactionStatusIdle
| typeof TransactionStatusInTransaction
| typeof TransactionStatusInError Given the TransactionStatus- prefix, the constants are unlikely to clash. The users could also inspect the It might be inconvenient to convert large enum, such as |
Yes, I think the point is exactly that the inconvenience isn't so bad, and the detriment in using enums is significant, in terms of code size and misalignment with a core TypeScript tenant. |
Currently
DatabaseError
is exported withexport type
, which means that the users of the library cannot import the class ofDatabaseError
itself to doinstanceof
checksThe following (example) code breaks at runtime when compiled to either ESM or CommonJS:
ESM error:
Similarly, one might want to access enums
ErrorLevel
andTransactionStatus
, but at runtime those cannot be imported due toexport type
The text was updated successfully, but these errors were encountered: