Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

TypeScript 6.0 can expose callback assignment errors in projects that previously relied on TypeScript’s non-strict default. The underlying rule is not new: strictFunctionTypes, introduced in TypeScript 2.6, checks function parameters contravariantly. It rejects a callback that accepts only a narrow input when its declared slot allows callers to pass a wider range of values.

Why does TypeScript say my callback type is not assignable?

A callback must be able to handle every value that its type says a caller may provide. If a callback accepts only Dog but its slot promises it can accept any Animal, a caller could pass a Cat. The callback might then try to use a dog-only property or method on that cat.

With strictFunctionTypes enabled, TypeScript rejects that unsafe assignment. For example, imagine Animal is the wider type and Dog is a subtype:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;

let handleAnimal: AnimalHandler;
let handleDog: DogHandler;

handleAnimal = handleDog; // Error: it may be called with a non-Dog
handleDog = handleAnimal; // Safe: it can handle every Dog

The direction matters. A function that accepts any Animal can stand in for one that will only be given Dog. A function that accepts only Dog cannot safely stand in for one that promises to handle every Animal. The official Type Compatibility handbook explains the safety issue: the wider callback slot may invoke the supplied function with a value its parameter cannot handle.

What does contravariance mean for callback parameters?

Under strict function checking, parameter types are compared in the opposite direction from the intuitive “more specific is always compatible” assumption. A callback that accepts a broader input is usable where a narrower input is promised, because it can handle that narrower value. The reverse is unsafe.

The official TypeScript 2.6 release notes introduced this behavior and give a simpler example: a function accepting only string cannot fill a callback slot that may be called with string | number. The slot’s caller is entitled to pass a number, so a string-only function would violate the contract. The strictFunctionTypes option reference shows this mismatch as a compiler error.

Rank #2
TypeScript Programming Language - Software Engineer & Coder T-Shirt
  • TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
  • TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem

Why can this appear after upgrading to TypeScript 6.0?

TypeScript 6.0 changes the default value of strict to true. A project that previously omitted strict and relied on its old default may therefore receive stricter checks, including callback parameter errors. This is a change in the default configuration, not the introduction of contravariant callback checking: TypeScript added strictFunctionTypes in version 2.6.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The TypeScript 6.0 release notes advise projects that want to preserve the previous non-strict default to set "strict": false explicitly. The option reference states that strictFunctionTypes is true when strict is true and false otherwise, unless the option is set directly.

How to diagnose the setting that changed

  1. Confirm the compiler version and invocation. Check which TypeScript version your build, editor, and command-line checks actually run. A build tool may use a different compiler or configuration than the editor.
  2. Check the effective project configuration. Inspect the tsconfig used by the failing command, including inherited configuration and any direct strict or strictFunctionTypes setting.
  3. Check whether a command-line option overrides the config. The effective setting depends on the compiler invocation, not only the file you expected it to read.
  4. Read the callback contract at the assignment site. Identify the full set of values the callback slot permits, then compare that with the values the supplied function can actually handle.

Choose a fix that matches the intended contract

Broaden the callback parameter when it can handle the wider input

If the callback is genuinely capable of handling every value its slot permits, declare that broader input. For instance, use (animal: Animal) => void when the implementation works for any Animal, rather than claiming it accepts only Dog. This makes the callback match the contract callers already rely on.

Correct the API declaration when callers should not pass the wider input

If the API is intended to call the callback only with dogs, its callback type should say so. Narrowing the API contract is appropriate only if the implementation and all callers truly guarantee that restriction; otherwise the declaration would hide a real runtime hazard.

Temporarily preserve non-strict behavior only as a migration choice

If the project is not ready to adopt strict checking, explicitly setting "strict": false preserves the previous default behavior described in the TypeScript 6.0 release notes. This changes checking across the project rather than repairing the callback contract, so it is a project-level compatibility choice, not a safety fix.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Avoid using a cast as the routine repair

A cast can silence the assignment error, but it does not make a callback capable of handling values it cannot handle. Use one only when you can independently establish the safety that the type checker can no longer verify.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Why can methods behave differently from function-valued properties?

strictFunctionTypes has a documented exception: parameters originating in method and constructor declarations are not checked with the same strict contravariant rule. As a result, a method signature and a function-valued property that appear similar can have different assignability behavior.

The TypeScript 2.6 release notes explain that this exception helps generic types such as Array<T> remain mostly covariant. It is a compatibility and type-system design rule, not evidence that methods are inherently safer at runtime. When designing an API, choose the declaration form deliberately rather than using a method signature merely to get past an error.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.