Skip to content
Sarj Code Standards

prefer-minimal-parameter-contract

Prefer the proven read-only subset for a private consumer parameter.

Why

A private scalar consumer can unnecessarily require data it never reads. Removing unused required fields accepts more structural values while preserving the original runtime object and producer information.

Fix

Review a smaller existing contract, Pick or interface containing the read fields and preserving readonly and optional modifiers. Change the consumer annotation; a new runtime projection has different identity and allocation costs. The safe fix removes only proven unused required property signatures from the private inline annotation; producer objects, calls and function bodies remain unchanged.

Examples

Before — flagged A scalar prefix does not require the object's unread email
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
const prefix = "Hello ";
function label(prefix: string, user: { email: string; name: string }): string {
return prefix + user.name;
}
label(prefix, account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
After — preferred Reading both object fields retains their requirements
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
const prefix = "Hello ";
function label(prefix: string, user: { email: string; name: string }): string {
return prefix + user.name + user.email;
}
label(prefix, account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
Autofix output
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
const prefix = "Hello ";
function label(prefix: string, user: { name: string }): string {
return prefix + user.name;
}
label(prefix, account);
Before — flagged Only the name crosses a local scalar helper call
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
function formatName(name: string): string {
return name.toUpperCase();
}
function label(user: { email: string; name: string }): string {
return formatName(user.name);
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
After — preferred The consumer still requires a separately read field
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
function formatName(name: string): string {
return name.toUpperCase();
}
function label(user: { email: string; name: string }): string {
return formatName(user.name) + user.email;
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
Autofix output
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
function formatName(name: string): string {
return name.toUpperCase();
}
function label(user: { name: string }): string {
return formatName(user.name);
}
label(account);
Before — flagged A required nullable field is never inspected
src/case.ts
export {};
const account = { name: "Ada", email: null };
function label(user: { email: string | null; name: string }): string {
return user.name;
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
After — preferred An inspected nullable field keeps its contract
src/case.ts
export {};
const account = { name: "Ada", email: null };
function label(user: { email: string | null; name: string }): string {
return user.email ?? user.name;
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
Autofix output
src/case.ts
export {};
const account = { name: "Ada", email: null };
function label(user: { name: string }): string {
return user.name;
}
label(account);
Before — flagged A nullable name remains while an unread email requirement is removed
src/case.ts
export {};
const account = { name: null, email: "a" };
function label(user: { readonly email: string; readonly name: string | null }): string {
return user.name ?? "";
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
After — preferred Both requirements remain when the nullable view reads email too
src/case.ts
export {};
const account = { name: null, email: "a" };
function label(user: { readonly email: string; readonly name: string | null }): string {
return user.name ?? user.email;
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
Autofix output
src/case.ts
export {};
const account = { name: null, email: "a" };
function label(user: { readonly name: string | null }): string {
return user.name ?? "";
}
label(account);
Before — flagged A private name consumer unnecessarily requires email
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
function label(user: { readonly email: string; readonly name: string }): string {
return user.name;
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
After — preferred Every required dependency is read
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
function label(user: { readonly email: string; readonly name: string }): string {
return user.name + user.email;
}
label(account);
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler"
}
}
Autofix output
src/case.ts
export {};
const account = { name: "Ada", email: "a" };
function label(user: { readonly name: string }): string {
return user.name;
}
label(account);