microsoft/TypeScript · error · TypeError
Object expected.
Error message
Object expected.
What it means
Runtime TypeError from the `__addDisposableResource` helper, emitted for the Explicit Resource Management feature (`using`/`await using`) when targeting runtimes that lack native support. The helper throws when `value` is neither `null`/`undefined` nor of type `"object"`/`"function"` — i.e. a primitive was bound with `using`.
Source
Thrown at src/compiler/factory/emitHelpers.ts:1387
const classPrivateFieldInHelper: UnscopedEmitHelper = {
name: "typescript:classPrivateFieldIn",
importName: "__classPrivateFieldIn",
scoped: false,
text: `
var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) {
if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object");
return typeof state === "function" ? receiver === state : state.has(receiver);
};`,
};
const addDisposableResourceHelper: UnscopedEmitHelper = {
name: "typescript:addDisposableResource",
importName: "__addDisposableResource",
scoped: false,
text: `
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
View on GitHub (pinned to b465fdbfe1)
Solutions
- Bind only objects (or null/undefined to skip) with `using`.
- Wrap the primitive in a small disposable object (`{ [Symbol.dispose]() {} }`).
- Set `target`/`lib` high enough that `using` is emitted natively and the helper is not generated.
- Type the `using` variable as a disposable so primitives are rejected at compile time.
Example fix
// before
using handle = 42; // primitive -> runtime TypeError
// after
using handle = { [Symbol.dispose]() { /* close */ } } as Disposable; Defensive patterns
Strategy: validation
Validate before calling
// Validate before binding (when types were bypassed):
function asDisposable<T>(v: T): T | null {
if (v === null || v === undefined) return null;
if (typeof v !== "object" && typeof v !== "function") throw new TypeError("Object expected");
return v;
} Type guard
function isDisposable(v: unknown): v is Disposable { return v !== null && v !== undefined && typeof (v as any)[Symbol.dispose] === "function"; } Prevention
- Never bind primitives to `using`/`await using`.
- Use `lib: ["disposable"]` / high `target` so native semantics apply.
- Type `using` bindings as `Disposable`/`AsyncDisposable` to catch mistakes statically.
When it happens
Trigger: Writing `using x = <primitive>` (number, string, boolean, symbol, bigint) where the downlevel helper runs (target without native `using`). Also reached by manual `__addDisposableResource(env, primitive, async)` calls.
Common situations: Binding a configuration value or numeric handle with `using` instead of an object; refactors that assign a primitive to a `using` variable; envs where `using` is transpiled because the target lacks the feature.
Related errors
- Symbol.asyncDispose is not defined.
- Symbol.dispose is not defined.
- Object not disposable.
- Private accessor was defined without a getter
- Cannot read private member from an object whose class did no
AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12).
Data as JSON: /api/errors/48bd9c4e9ff87bc6.
Report an issue: GitHub.