![Create React App Officially Deprecated Amid React 19 Compatibility Issues](https://cdn.sanity.io/images/cgdhsj6q/production/04fa08cf844d798abc0e1a6391c129363cc7e2ab-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Create React App Officially Deprecated Amid React 19 Compatibility Issues
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
@perfective/common
Advanced tools
Common types and functions for perfective development in TypeScript
npm install @perfective/common
After the installation you can read the full compiled documentation in the node_modules/@perfective/common/docs.html
.
The @perfective/common
package facilitates writing highly readable functional code.
It focuses on providing functions to handle ECMAScript types
and to compose functions together easily.
The @perfective/common/maybe
package
provides a Maybe monad implementation.
It allows you to write and compose functions that accept only present (defined and non-null) values.
It helps avoid additional complexity and noise when handling null
and undefined
values.
For example, consider you have the User
and Name
types below and want to output a user’s full name.
interface User {
name?: Name;
}
interface Name {
first: string;
last: string;
}
If you write functions that have to handle null
and undefined
values,
then you would have to write something like this:
function userNameOutput(user: User | null | undefined): string {
if (isPresent(user)) {
const name = fullName(user.name);
if (isPresent(name)) {
return name;
}
}
throw new Error('User name is unknown');
}
function fullName(name: Name | null | undefined): string | null {
if (isPresent(name)) {
const trimmed = `${name.first} ${name.last}`.trim();
if (isNotEmpty(trimmed)) {
return trimmed;
}
}
return null;
}
Using the Maybe
monad,
you can write simpler and more readable functions:
import { panic } from '@perfective/common/error';
import { just, Maybe, maybe } from '@perfective/common/maybe';
import { isNotEmpty, trim } from '@perfective/common/string';
function userNameOutput(user: User | null | undefined): string {
return maybe(user)
.pick('name') // <1>
.onto(fullName) // <2>
.or(panic('User name is unknown')); // <3>
}
function fullName(name: Name): Maybe<string> {
return just(`${name.first} ${name.last}`)
.to(trim) // <4>
.that(isNotEmpty); // <5>
}
Maybe.pick()
provides a strictly typed "optional chaining" of the Maybe.value
properties.Maybe.onto()
(flat) maps a Maybe.value
to another Maybe
.Maybe.or()
extracts a value
from the Maybe
with a given fallback.
(or allows to throw an error).Maybe.that()
filters a value inside Maybe
.Maybe.to()
maps a value
inside Maybe
using a given callback.In addition to these methods,
the Maybe
monad also has:
Maybe.into()
,
Maybe.which()
,
Maybe.when()
,
Maybe.otherwise()
,
and Maybe.through()
methods.
Read more about the Maybe
monad and other
@perfective/common/maybe
functions
in the package documentation.
The @perfective/common/result
package
provides the Result
monad implementation
(as a concrete case of the Either monad).
It allows developers to increase the reliability of their code by treating errors as valid part of a function output.
A Result
instance can be either a Success
or a Failure
.
If a Result
is a Success
, a computation proceeds to the next step.
In case of a Failure
, all further computations are skipped until the recovery or exit from the computation.
The Result
monad is similar to the Maybe
monad,
but unlike Maybe
, a Result
contains a reason for its Failure
.
The Result
monad is also similar to the Promise
(as a Promise
can either be "resolved" or "rejected").
But, unlike Promise
, the Result
chain is synchronous.
For example, consider you have an HTTP endpoint to return user data stored in the database.
The purpose of the endpoint is to map a given (unsafe) user ID input to a User
.
Assume you have the following functions
export interface User {
// User data
}
/** Returns `true` if the active user has admin access. */
declare function hasAdminAccess(): boolean;
/** Builds an SQL query to load a user with a given `id`. */
declare function userByIdQuery(id: number): string;
/** Sends a given `sql` to the database and returns a User. */
declare function userQueryResult(sql: string): Promise<User>;
/** Logs a given error */
declare function logError(error: Error): void;
If you write regular imperative code you may have something like this:
/** @throws Error if a given id is invalid. */
function validUserId(id: unknown): number {
if (typeof id !== 'string') {
throw new TypeError('Input must be a string');
}
const userId = decimal(id);
if (userId === null) {
throw new Error('Failed to parse user id');
}
if (!Number.isInteger(userId) || userId <= 0) {
throw new Error('Invalid user id');
}
return userId;
}
async function userResponseById(id: unknown): Promise<User> {
try {
return userForQuery(
userByIdQuery(
validUserId(id), // (1)
),
);
} catch (error: unknown) {
logError(error as Error);
throw error as Error;
}
}
validUserId()
indicates that it throws an error only as a JSDoc.
TypeScript compiler does not check that the code should be wrapped into the try-catch
block.Using the Result
monad and functions from the @perfective/common
subpackages you can write the same code as:
import { isNotNull } from '@perfective/common';
import { typeError } from '@perfective/common/error';
import { naught } from '@perfective/common/function';
import { decimal, isNonNegativeInteger } from '@perfective/common/number';
import { rejected } from '@perfective/common/promise';
import { Result, success } from '@perfective/common/result';
import { isString } from '@perfective/common/string';
function validUserId(id: unknown): Result<number> {
return success(id)
.which(isString, typeError('Input must be a string')) // (1)
.to(decimal)
.which(isNotNull, 'Failed to parse user ID') // (2)
.that(isNonNegativeInteger, 'Invalid user ID'); // (3)
}
async function userResponseById(id: unknown): Promise<User> {
return success(id)
.when(hasAdminAccess, 'Access Denied') // (4)
.onto(validUserId) // (5)
.to(userByIdQuery)
.through(naught, logError) // (6)
.into(userForQuery, rejected); // (7)
}
Result.which()
applies a type guard and narrows the Result.value
type.decimal()
returns number | null
, so another type guard is required.Result.that()
checks if the Success.value
satisfies a given predicate.Result.when()
checks an external condition.Result.onto()
allows a different Result
object to be returned
(in this case, the Result
of the validUserId()
function).Result.through()
runs a given procedure
(a no-op naught()
function for the Success
).Result.into()
allows the completion (folding) of the Result
chain computation and switch to a different type.In addition to the methods used in the example above,
the Result
monad also provides
Result.or()
and
Result.otherwise()
methods.
Read more about the Result
monad and other
@perfective/common/result
functions in the
package documentation.
The ECMA Error
class does not store a previous error.
This is inconvenient, as it requires either manually adding a previous error message to a new error.
Or worse, skip providing the previous error altogether.
Chaining previous errors is helpful for debugging.
Especially in async environments, when most of the stack trace is full of useless function calls like next()
or on the frontend with packed code and renamed functions.
The @perfective/common/error
package
provides the Exception
class
to make logging and debugging of production code easier.
It supports three features:
.Using the Exception
class and its constructors.
import { caughtError, causedBy, chained, exception } from '@perfective/common/error';
interface FetchRequest {
method: string;
url: string;
}
interface User {}
function numberInput(input: string): number {
const id = Number(input);
if (Number.isNaN(id)) {
throw exception('Input {{value}} is not a number', { // <1>
value: input,
});
}
return id;
}
function userRequest(id: string): FetchRequest {
try {
const userId = numberInput(id);
return {
method: 'GET',
url: `user/${userId}`,
};
} catch (error: unknown) { // <2>
throw causedBy(caughtError(error), 'Invalid user id {{id}}', { // <3>
id,
});
}
}
async function userResponse(request: FetchRequest): Promise<User> {
return fetch(request.url, {
method: request.method,
});
}
async function user(id: string): Promise<User> {
return Promise.resolve(id).then(userRequest).then(userResponse).catch(
chained('Failed to load user {{id}}', { // <4>
id,
}),
);
}
exception()
function to instantiate an initial Exception
without previous errors.caughtError()
function to wrap a possible non-Error
value.try-catch
block,
use the causedBy()
function to create an Exception
with a previous error.chained()
function to create a callback to chain an Error
(for example, in Promise
or a Result
).When you want to output a chained Exception
,
you can use the Exception.toString()
method.
For the example above, the output may look like this:
Exception: Failed to load user `A`
- Exception: Invalid user id `A`
- Exception: Input `A` is not a number
If you want to log an Exception
for debugging purposes, use the chainedStack()
function.
It will return a similar chain of messages as above,
but each message will also contain a stack trace for each error.
Read more about the functions to handle the built-in JS errors and the Exception
class in the
@perfective/common/error
package documentation.
Packages are organized and named around their primary type:
@perfective/common
— functions and types to handle types (e.g., TypeGuard
interface), null
, undefined
, and void
values.@perfective/common/array
— functions and types for handling
arrays.@perfective/common/boolean
— functions and types to handle
boolean
values.@perfective/common/date
— functions and types to handle
Date
values.@perfective/common/error
— functions and types to handle
Error
and related classes.@perfective/common/function
— functions and types for functional programming.@perfective/common/match
— functions and types for a functional style switch-case
.@perfective/common/maybe
— a Maybe
monad (Option type) implementation.@perfective/common/number
— functions and types to handle
numbers.@perfective/common/object
— functions and types for handling the
Object
class.@perfective/common/promise
— functions and types to handle the
Promise
class.@perfective/common/result
— a Result
monad (Result type) implementation.@perfective/common/string
— functions and types to handle
strings.The packages have full unit test coverage.
The ROADMAP.adoc
file describes
how built-in JavaScript objects and methods are covered by the @perfective/common
package.
This package starts its versioning from 0.7.0
,
as it continues versioning after the deprecated packages:
@perfective/array
(v0.4.0
);@perfective/error
(v0.3.0
);@perfective/fp
(v0.6.0
);@perfective/identity
(v0.2.0
);@perfective/match
(v0.3.0
);@perfective/maybe
(v0.6.0
);@perfective/object
(v0.4.0
);@perfective/real
(v0.6.0
);@perfective/string
(v0.3.0
);@perfective/value
(v0.3.0
).FAQs
Common types and functions for perfective development in TypeScript
The npm package @perfective/common receives a total of 1 weekly downloads. As such, @perfective/common popularity was classified as not popular.
We found that @perfective/common demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.