Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions cell-brand-bug-reproduction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Minimal test case for OpaqueRef<Cell<T>> CELL_BRAND conflict
*
* PROBLEM: OpaqueRef<T> recursively wraps ALL properties of T (including [CELL_BRAND]).
* When T is Cell<number>, this creates conflicting types for [CELL_BRAND]:
* - OpaqueCell<Cell<number>> has [CELL_BRAND]: "opaque"
* - { counter: OpaqueRef<Cell<number>> } creates [CELL_BRAND]: OpaqueRef<"cell">
* The intersection reduces to 'never'.
*/

import { type Cell, type OpaqueRef } from "./packages/api/index.ts";

interface State {
counter: Cell<number>;
}

// Accessing properties on OpaqueRef<Cell<T>> fails type-checking
function _reproducesBug(state: OpaqueRef<State>) {
state.counter.set(state.counter.get() + 1);
// ^^^ Property 'set' does not exist on type 'never'
// ^^^ Property 'get' does not exist on type 'never'
// ERROR: The intersection 'OpaqueRef<Cell<number>>' was reduced to 'never'
// because property '[CELL_BRAND]' has conflicting types in some constituents.
}
5 changes: 4 additions & 1 deletion packages/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,14 @@ export interface WriteonlyCell<T>
* OpaqueRef is a variant of OpaqueCell with recursive proxy behavior.
* Each key access returns another OpaqueRef, allowing chained property access.
* This is temporary until AST transformation handles .key() automatically.
*
* Note: Symbol keys (like CELL_BRAND) are excluded from mapping to avoid
* brand conflicts when wrapping branded cells.
*/
export type OpaqueRef<T> =
& OpaqueCell<T>
& (T extends Array<infer U> ? Array<OpaqueRef<U>>
: T extends object ? { [K in keyof T]: OpaqueRef<T[K]> }
: T extends object ? { [K in keyof T as K extends symbol ? never : K]: OpaqueRef<T[K]> }
: T);

// ============================================================================
Expand Down
Loading