@adapter
Class decorator that prevents the decorated class from being instantiated. It is a pure marker for static-only utility classes — typically the parsers that translate raw JSON envelopes (ForeignData) into typed DTOs.
Importing
import { adapter } from '@basmilius/http-client';Usage
import { adapter, type ForeignData } from '@basmilius/http-client';
import { UserDto } from '../dto/UserDto';
@adapter
export class UserAdapter {
static parseUser(data: ForeignData): UserDto {
return new UserDto(
data.id,
data.email,
data.full_name
);
}
}
UserAdapter.parseUser(payload); // ok
new UserAdapter(); // throws ErrorWhat @adapter does
The decorator returns a subclass of the target whose constructor throws:
export default function <T extends Constructor>(Parent: T): T {
return class extends Parent {
constructor(...args: any[]) {
throw new Error('@adapter: cannot create instance of class.');
}
} as T;
}Static methods are inherited unchanged, so call sites continue to use the class name. Because the constructor always throws, new MyAdapter() becomes a runtime error that surfaces immediately during development.
Why static-only
Adapter classes act as namespaces for parser functions. They do not own any state, and an instance would be a programming error. Decorating them with @adapter formalises that contract:
- Static methods can be passed by reference to
runAdapter,runArrayAdapterandrunPaginatedAdapter. - Adapter methods can call into other adapters statically (e.g.
DateTimeAdapter.parseDateTime(data.created_on)). - The class name remains available at runtime, which is helpful for stack traces and tooling.
Composition
Adapters compose through plain function calls. A common pattern is a small optional helper that short-circuits on null / undefined:
// util/optional.ts
export default function <T, U>(value: U, adapterFn: (value: U) => T): T {
if (value === undefined || value === null) {
return null as T;
}
return adapterFn(value);
}@adapter
export class AuthAdapter {
static parseUserToken(data: ForeignData): UserTokenDto {
return new UserTokenDto(
data.token,
data.type,
DateTimeAdapter.parseDateTime(data.created_on),
DateTimeAdapter.parseDateTime(data.expires_on),
optional(data.user, AuthAdapter.parseUser)
);
}
static parseUser(data: ForeignData): UserDto {
return new UserDto(
data.id,
data.email,
data.full_name,
optional(data.picture, FileSystemAdapter.parsePicture)
);
}
}optional is a consumer-side convention rather than part of the package — it pairs naturally with the adapter pattern.
Type signature
declare function adapter<T extends Constructor>(Parent: T): T;See also
HttpAdapter— the canonical example shipped with the package.@dto— the other class-level decorator in the package.runAdapter— feeds a parsed JSON body through an adapter method.