R13BREAKINGfunctionGeneric Constraint Narrowed
Flags when a generic type parameter gains a more restrictive constraint. Callers using types that no longer satisfy the new constraint will fail compilation.
Applies to
TypeScriptJavaRust
Why it matters
Generic constraints define the contract for type parameters. Narrowing a constraint means type arguments that previously satisfied the constraint may no longer be valid.
Example
Before (unconstrained generic)
// transform.ts
export function transform<T>(value: T): T {
return value;
}
// caller — works with any type
transform(42);
transform("hello");
transform({ x: 1 });After (constrained to Serializable)
// transform.ts (BREAKING)
export function transform<T extends Serializable>(value: T): T {
return serialize(value);
}
// caller — primitive types may not implement Serializable
transform(42); // ERROR if number doesn't satisfy SerializableWhat you see in the terminal
$ npx dg check
[BREAKING] transform (signature_change)
src/transform.ts:2
Generic type parameter 'T' constraint narrowed. Type arguments not satisfying 'Serializable' will fail.How detection works
The classifier compares the generic type parameters' constraint fields in old and new signatures. If a constraint is added or made more restrictive, the rule fires.
Real-world scenario
A utility function gains a Serializable constraint because the implementation now needs to serialize the value. All callers passing primitive or non-Serializable types will break.
Edge cases
- Adding a constraint where none existed is the most common case
- Changing 'extends object' to 'extends Record
' is narrowing - Only applies to languages with generics: TypeScript, Java, Rust