TypeScript Type Immutability Checker
is-immutable-type is a TypeScript utility library designed to statically analyze the immutability of TypeScript types within a given program context. It provides detailed classifications for types, distinguishing between `Immutable` (deeply read-only, no modifications possible), `ReadonlyDeep` (deeply immutable data, but methods are not), `ReadonlyShallow` (shallowly immutable, but deep values are not), `Mutable` (shallowly mutable), and `Unknown` (immutability could not be determined). The library is currently on version 5.0.1 and maintains an active release cadence, with several major and minor versions released recently, indicating continuous development. Its key differentiators include precise immutability definitions and a robust override mechanism, allowing developers to specify immutability for types where static analysis alone might be insufficient. It is typically integrated into TypeScript tooling like ESLint plugins for advanced type-aware linting.
Common errors
-
Error: "is-immutable-type" requires "typescript-eslint" v8.x but found v7.x.
cause Using `is-immutable-type` v5.x or newer with an older `typescript-eslint` installation.fixUpgrade `typescript-eslint` to version 8.x or higher using `npm install --save-dev typescript-eslint@latest` or `yarn add --dev typescript-eslint@latest`. -
TypeError: Cannot read properties of undefined (reading 'getTypeChecker')
cause The `program` argument passed to `getTypeImmutability` (or similar functions) is `undefined` or not a valid `ts.Program` instance.fixEnsure you are passing a properly initialized `ts.Program` object, which is usually obtained from `typescript.createProgram()` or from a tooling context like an ESLint rule's parser services. -
TS2345: Argument of type 'boolean' is not assignable to parameter of type 'Immutability'.
cause Attempting to compare `Immutability.Unknown` directly with `===`, which returns `false` and might be incorrectly used in a conditional where an `Immutability` value is expected.fixUse the `isUnknown(immutability)` helper function instead of direct `immutability === Immutability.Unknown` comparison.
Warnings
- breaking Version 5.0.0 dropped support for `typescript-eslint` v7. Users must upgrade their `typescript-eslint` peer dependency to v8 to continue using `is-immutable-type`.
- breaking Version 4.0.0 introduced breaking changes related to how types are handled, specifically concerning the 'Type' parameter in some core functions, likely due to the new 'allow for ignoring types' feature. While the changelog is terse, it suggests API adjustments for type resolution or processing.
- gotcha When checking for `Immutability.Unknown`, direct comparison using `===` will always return `false`. Always use the `isUnknown()` helper function provided by the library.
- gotcha The library operates on a `ts.Program` instance to analyze types. This means it's designed to be used within a TypeScript compiler context, such as an ESLint plugin, a custom transformer, or other tooling that provides access to the full TypeScript AST and type checker.
Install
-
npm install is-immutable-type -
yarn add is-immutable-type -
pnpm add is-immutable-type
Imports
- getTypeImmutability
const getTypeImmutability = require('is-immutable-type').getTypeImmutability;import { getTypeImmutability } from 'is-immutable-type'; - Immutability
const Immutability = require('is-immutable-type').Immutability;import { Immutability } from 'is-immutable-type'; - isReadonlyDeep
import isReadonlyDeep from 'is-immutable-type/isReadonlyDeep';
import { isReadonlyDeep } from 'is-immutable-type'; - isUnknown
import { isUnknown } from 'is-immutable-type';
Quickstart
import { Immutability, getTypeImmutability, isReadonlyDeep, isUnknown } from 'is-immutable-type';
import { hasType } from 'ts-api-utils';
import type ts from 'typescript';
/**
* Demonstrates how to get and interpret the immutability of a TypeScript type.
* This example requires a TypeScript Program instance and a Node from its AST.
* @param program The TypeScript program instance.
* @param node The AST node whose type's immutability is to be checked.
*/
function checkNodeImmutability(program: ts.Program, node: ts.Node) {
const typeNodeOrType = hasType(node)
? // Use the TypeNode if it's available.
node.type
: // Otherwise, get the Type from the checker.
program.getTypeChecker().getTypeAtLocation(node);
// Ensure a type was found before proceeding
if (!typeNodeOrType) {
console.log(`Could not determine type for node at position ${node.pos}.`);
return;
}
const immutability = getTypeImmutability(program, typeNodeOrType);
if (isUnknown(immutability)) {
console.log(`Node at ${node.pos} has 'Unknown' immutability.`);
} else if (isReadonlyDeep(immutability)) {
console.log(`Node at ${node.pos} has 'ReadonlyDeep' or 'Immutable' immutability.`);
} else if (immutability === Immutability.ReadonlyShallow) {
console.log(`Node at ${node.pos} has 'ReadonlyShallow' immutability.`);
} else if (immutability === Immutability.Mutable) {
console.log(`Node at ${node.pos} has 'Mutable' immutability.`);
} else {
console.log(`Node at ${node.pos} has an unexpected immutability state.`);
}
}
// Example usage within a dummy context (requires a real ts.Program and ts.Node)
// For a runnable example, you'd typically run this within an ESLint rule or custom TS transform.
// const dummyProgram = /* A real ts.Program instance */;
// const dummyNode = /* A real ts.Node instance */;
// if (dummyProgram && dummyNode) {
// checkNodeImmutability(dummyProgram, dummyNode);
// } else {
// console.log("Please provide a valid TypeScript program and node for this example.");
// }