Skip to content

Commit

Permalink
fix: class type narrowing
Browse files Browse the repository at this point in the history
  • Loading branch information
leemhenson committed Aug 22, 2018
1 parent c55b0c2 commit 5a16d77
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 18 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Typescript wrapper around node-postgres
:wrench: chore
:notebook: docs

### v8.0.0
- :bug: fix broken nominal typing on classes by changing `public readonly _T = "<class name>"`
to `public readonly _<class name>: void`.
- :boom: as a result of fixing nominal typing, `makeConnectionPool` now returns an error union
of `PgPoolCreationError | PgTypeParserSetupError` instead of `PgPoolCreationError`. Other errors
may occur in application code as a result of the error classes now being correctly differentiated.

## v7.0.0
- :boom: `[email protected]`
- :boom: `[email protected]`
Expand Down
20 changes: 10 additions & 10 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as t from "io-ts";
import { QueryConfig } from "pg";

export class PgPoolCheckoutError extends Error {
public readonly _T = Symbol("PgPoolCheckoutError");
public readonly _PgPoolCheckoutError: void;

constructor(public readonly error: t.mixed) {
super("Unable to checkout a connection from the pool.");
Expand All @@ -17,7 +17,7 @@ export const isPoolCheckoutError = (e: t.mixed): e is PgPoolCheckoutError =>
e instanceof PgPoolCheckoutError;

export class PgPoolCreationError extends Error {
public readonly _T = Symbol("PgPoolCreationError");
public readonly _PgPoolCreationError: void;

constructor(public readonly error: t.mixed) {
super("Unable to create a connection pool.");
Expand All @@ -32,7 +32,7 @@ export const isPoolCreationError = (e: t.mixed): e is PgPoolCreationError =>
e instanceof PgPoolCreationError;

export class PgPoolShutdownError extends Error {
public readonly _T = Symbol("PgPoolShutdownError");
public readonly _PgPoolShutdownError: void;

constructor(public readonly error: t.mixed) {
super("Unable to shutdown a connection pool.");
Expand All @@ -47,7 +47,7 @@ export const isPoolShutdownError = (e: t.mixed): e is PgPoolShutdownError =>
e instanceof PgPoolShutdownError;

export class PgDriverQueryError extends Error {
public readonly _T = Symbol("PgDriverQueryError");
public readonly _PgDriverQueryError: void;

constructor(public readonly error: t.mixed, public readonly query: QueryConfig) {
super("Error raised by node-pg during query execution.");
Expand All @@ -63,7 +63,7 @@ export const isDriverQueryError = (e: t.mixed): e is PgDriverQueryError =>
e instanceof PgDriverQueryError;

export class PgRowCountError extends Error {
public readonly _T = Symbol("PgRowCountError");
public readonly _PgRowCountError: void;

constructor(
public readonly query: QueryConfig,
Expand All @@ -82,7 +82,7 @@ export const makeRowCountError = (query: QueryConfig) => (e: t.mixed) =>
export const isRowCountError = (e: t.mixed): e is PgRowCountError => e instanceof PgRowCountError;

export class PgRowValidationError extends Error {
public readonly _T = Symbol("PgRowValidationError");
public readonly _PgRowValidationError: void;

constructor(
public readonly type: t.Any,
Expand All @@ -102,7 +102,7 @@ export const isRowValidationError = (e: t.mixed): e is PgRowValidationError =>
e instanceof PgRowValidationError;

export class PgTypeParserSetupError extends Error {
public readonly _T = Symbol("PgTypeParserSetupError");
public readonly _PgTypeParserSetupError: void;

constructor(public readonly error: t.mixed) {
super("Type parser setup failed.");
Expand All @@ -117,7 +117,7 @@ export const isTypeParserSetupError = (e: t.mixed): e is PgTypeParserSetupError
e instanceof PgTypeParserSetupError;

export class PgTransactionRollbackError extends Error {
public readonly _T = Symbol("PgTransactionRollbackError");
public readonly _PgTransactionRollbackError: void;

constructor(public readonly rollbackError: t.mixed, public readonly connectionError: t.mixed) {
super("A ROLLBACK was requested but not successfully completed.");
Expand All @@ -133,7 +133,7 @@ export const isTransactionRollbackError = (e: t.mixed): e is PgTransactionRollba
e instanceof PgTransactionRollbackError;

export class PgUnhandledConnectionError extends Error {
public readonly _T = Symbol("PgUnhandledConnectionError");
public readonly _PgUnhandledConnectionError: void;

constructor(public readonly error: t.mixed) {
super("An unhandled error was raised by a connection.");
Expand All @@ -148,7 +148,7 @@ export const isUnhandledConnectionError = (e: t.mixed): e is PgUnhandledConnecti
e instanceof PgUnhandledConnectionError;

export class PgUnhandledPoolError extends Error {
public readonly _T = Symbol("PgUnhandledPoolError");
public readonly _PgUnhandledPoolError: void;

constructor(public readonly error: t.mixed) {
super("An unhandled error was raised by a connection pool.");
Expand Down
2 changes: 1 addition & 1 deletion src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { constant } from "fp-ts/lib/function";
import { tryCatch } from "fp-ts/lib/TaskEither";
import * as pg from "pg";
import { makeTypeParserSetupError, PgTypeParserSetupError } from "./errors";
import { makeTypeParserSetupError } from "./errors";
import { parseInterval } from "./pgTypes/interval";
import { TypeParser, TypeParsers } from "./types";
import { SQL } from "./utils/sql";
Expand Down
2 changes: 0 additions & 2 deletions src/pgTypes/interval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ interface PGNamedIntervalObject<T> {
years: T | undefined;
}

type PGIntervalField = keyof PGNamedIntervalObject<any>;

interface PGIntervalObject<T> extends Record<string, T | undefined>, PGNamedIntervalObject<T> {}

const NEGATION_INDEX = 8;
Expand Down
6 changes: 4 additions & 2 deletions src/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
makeUnhandledConnectionError,
makeUnhandledPoolError,
PgPoolCreationError,
PgTypeParserSetupError,
PgUnhandledConnectionError,
} from "./errors";
import { setupParsers } from "./parser";
Expand All @@ -35,7 +36,7 @@ import {

export const makeConnectionPool = (
poolConfig: ConnectionPoolConfig,
): TaskEither<PgPoolCreationError, ConnectionPool> => {
): TaskEither<PgPoolCreationError | PgTypeParserSetupError, ConnectionPool> => {
const { onError, parsers } = poolConfig;

const poolIo = ioTryCatch(() => {
Expand All @@ -55,10 +56,11 @@ export const makeConnectionPool = (
);

return fromIOEither(poolIo)
.mapLeft<PgPoolCreationError | PgTypeParserSetupError>(identity)
.chain(pool =>
fromNullable(parsers)
.map(setupParsers(pool))
.getOrElse(taskEither.of(pool)),
.getOrElse(taskEither.of<PgTypeParserSetupError, pg.Pool>(pool)),
)
.map(wrapConnectionPool);
};
Expand Down
11 changes: 10 additions & 1 deletion tests/integration/support/testTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
camelCasedQueries,
ConnectedEnvironment,
makeConnectionPool,
PgPoolCheckoutError,
PgPoolCreationError,
PgTypeParserSetupError,
PgUnhandledConnectionError,
SQL,
} from "../../../src";
import { QueryNoneError } from "../../../src/query";
Expand Down Expand Up @@ -35,7 +38,13 @@ export const connectionTest = <L, A>(

const prepareDb = createTable.chain(() => truncate("units")).chain(() => insertUnits);

type ProgramError = PgPoolCreationError | QueryNoneError | L;
type ProgramError =
| PgPoolCheckoutError
| PgPoolCreationError
| PgTypeParserSetupError
| PgUnhandledConnectionError
| QueryNoneError
| L;

return makeConnectionPool(getPoolConfig(connectionString))
.mapLeft<ProgramError>(identity)
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ConnectedEnvironment,
isRowCountError,
PgRowCountError,
PgRowValidationError,
SQL,
TransactionError,
withTransaction,
Expand Down Expand Up @@ -63,6 +64,9 @@ describe("transaction", () => {
)
.chain(fromReader)
.chain(fromTaskEither)
.mapLeft<TransactionError<QueryNoneError> | UnexpectedRightError | PgRowValidationError>(
identity,
)
.chain(() => queryAny(Unit, SQL`SELECT * FROM units WHERE id >= 10 ORDER BY id`))
.map(units => {
expect(units).toHaveLength(0);
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
"compilerOptions": {
"declaration": true,
"module": "commonjs",
"noErrorTruncation": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "build",
"pretty": true,
Expand Down
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"interface-name": [
false
],
"max-classes-per-file": false
"max-classes-per-file": false,
"variable-name": false
},
"rulesDirectory": []
}

0 comments on commit 5a16d77

Please sign in to comment.