or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

computed.mdeffect-scopes.mdeffects.mdindex.mdreactive-objects.mdrefs.mdutilities.mdwatchers.md

utilities.mddocs/

0

# Utility Functions

1

2

Utility functions provide additional tools for working with reactive values, type conversions, and advanced reactive operations that don't fit into the core reactive categories.

3

4

## Capabilities

5

6

### toRef()

7

8

Normalizes values/refs/getters into refs, or creates a ref for a property on a reactive object.

9

10

```typescript { .api }

11

/**

12

* Converts a value to a ref

13

* @param value - Value to convert to ref

14

* @returns A ref containing the value

15

*/

16

function toRef<T>(value: T): ToRef<T>;

17

18

/**

19

* Creates a ref for a property on a reactive object

20

* @param object - Reactive object

21

* @param key - Property key

22

* @returns A ref linked to the object property

23

*/

24

function toRef<T extends object, K extends keyof T>(object: T, key: K): ToRef<T[K]>;

25

26

/**

27

* Creates a ref for a property with a default value

28

* @param object - Reactive object

29

* @param key - Property key

30

* @param defaultValue - Default value if property is undefined

31

* @returns A ref linked to the object property with default

32

*/

33

function toRef<T extends object, K extends keyof T>(

34

object: T,

35

key: K,

36

defaultValue: T[K]

37

): ToRef<Exclude<T[K], undefined>>;

38

```

39

40

**Usage Examples:**

41

42

```typescript

43

import { reactive, toRef, effect } from "@vue/reactivity";

44

45

const state = reactive({

46

count: 0,

47

user: { name: "Alice" },

48

tags: ["vue", "reactivity"]

49

});

50

51

// Create refs from reactive object properties

52

const countRef = toRef(state, "count");

53

const userRef = toRef(state, "user");

54

55

// Refs maintain connection to original object

56

effect(() => {

57

console.log(`Count ref: ${countRef.value}`);

58

});

59

60

countRef.value = 5; // Updates state.count and triggers effect

61

console.log(state.count); // 5

62

63

// Convert plain value to ref

64

const messageRef = toRef("Hello World");

65

console.log(messageRef.value); // "Hello World"

66

67

// Property ref with default value

68

const unknownProperty = toRef(state, "missing" as any, "default");

69

console.log(unknownProperty.value); // "default"

70

```

71

72

### toRefs()

73

74

Converts a reactive object to a plain object where each property is a ref that maintains reactivity connection to the original object.

75

76

```typescript { .api }

77

/**

78

* Converts a reactive object to refs for each property

79

* @param object - Reactive object to convert

80

* @returns Object with ref properties

81

*/

82

function toRefs<T extends object>(object: T): ToRefs<T>;

83

84

type ToRefs<T = any> = {

85

[K in keyof T]: ToRef<T[K]>;

86

};

87

```

88

89

**Usage Examples:**

90

91

```typescript

92

import { reactive, toRefs, effect } from "@vue/reactivity";

93

94

const state = reactive({

95

count: 0,

96

message: "Hello",

97

user: { name: "Alice" },

98

items: [1, 2, 3]

99

});

100

101

// Convert to refs - useful for destructuring without losing reactivity

102

const { count, message, user, items } = toRefs(state);

103

104

// All refs maintain connection to original state

105

effect(() => {

106

console.log(`${message.value}: ${count.value}`);

107

});

108

109

count.value = 10; // Updates state.count and triggers effect

110

message.value = "Count"; // Updates state.message and triggers effect

111

112

// Useful in composition functions

113

function useCounter(initialValue = 0) {

114

const state = reactive({

115

count: initialValue,

116

doubled: computed(() => state.count * 2)

117

});

118

119

const increment = () => state.count++;

120

const decrement = () => state.count--;

121

122

// Return refs for easy destructuring

123

return {

124

...toRefs(state),

125

increment,

126

decrement

127

};

128

}

129

130

const { count: myCount, doubled } = useCounter(5);

131

console.log(myCount.value); // 5

132

console.log(doubled.value); // 10

133

```

134

135

### toValue()

136

137

Normalizes values/refs/getters to values. Similar to `unref` but also handles getter functions.

138

139

```typescript { .api }

140

/**

141

* Normalizes values/refs/getters to values

142

* @param source - A ref, getter function, or plain value

143

* @returns The resolved value

144

*/

145

function toValue<T>(source: MaybeRefOrGetter<T>): T;

146

147

type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T);

148

```

149

150

**Usage Examples:**

151

152

```typescript

153

import { ref, computed, toValue } from "@vue/reactivity";

154

155

const count = ref(42);

156

const doubled = computed(() => count.value * 2);

157

const getValue = () => 100;

158

const plainValue = 200;

159

160

// Works with all types

161

console.log(toValue(count)); // 42

162

console.log(toValue(doubled)); // 84

163

console.log(toValue(getValue)); // 100

164

console.log(toValue(plainValue)); // 200

165

166

// Useful for flexible function parameters

167

function useFlexibleValue<T>(source: MaybeRefOrGetter<T>) {

168

const value = toValue(source);

169

console.log("Current value:", value);

170

return value;

171

}

172

173

useFlexibleValue(count); // Works with ref

174

useFlexibleValue(doubled); // Works with computed

175

useFlexibleValue(getValue); // Works with getter

176

useFlexibleValue(300); // Works with plain value

177

```

178

179

### markRaw()

180

181

Marks an object so that it will never be converted to a proxy. Useful for performance optimization or compatibility with third-party libraries.

182

183

```typescript { .api }

184

/**

185

* Marks an object to never be converted to a proxy

186

* @param value - Object to mark as raw

187

* @returns The same object with a skip marker

188

*/

189

function markRaw<T extends object>(value: T): Raw<T>;

190

191

type Raw<T> = T & { [RawSymbol]?: true };

192

```

193

194

**Usage Examples:**

195

196

```typescript

197

import { reactive, markRaw, isReactive } from "@vue/reactivity";

198

199

// Large dataset that doesn't need reactivity

200

const largeDataSet = markRaw(new Array(10000).fill(0).map((_, i) => ({ id: i })));

201

202

// Third-party library instance

203

const thirdPartyLib = markRaw(new SomeLibrary());

204

205

// Date objects (often don't need reactivity)

206

const timestamp = markRaw(new Date());

207

208

const state = reactive({

209

count: 0,

210

data: largeDataSet, // Won't be made reactive

211

lib: thirdPartyLib, // Won't be made reactive

212

created: timestamp, // Won't be made reactive

213

config: { theme: "dark" } // Will be made reactive

214

});

215

216

console.log(isReactive(state.data)); // false

217

console.log(isReactive(state.lib)); // false

218

console.log(isReactive(state.created)); // false

219

console.log(isReactive(state.config)); // true

220

221

// Performance benefit: mutations to marked raw objects don't trigger effects

222

state.data.push({ id: 10000 }); // No reactive overhead

223

224

// Map and Set examples

225

const nonReactiveMap = markRaw(new Map([

226

["key1", "value1"],

227

["key2", "value2"]

228

]));

229

230

const reactiveState = reactive({

231

lookup: nonReactiveMap // Map operations won't trigger reactivity

232

});

233

234

// Direct Map operations are fast and don't trigger effects

235

reactiveState.lookup.set("key3", "value3");

236

reactiveState.lookup.delete("key1");

237

```

238

239

### customRef()

240

241

Creates a customized ref with explicit control over dependency tracking and updates.

242

243

```typescript { .api }

244

/**

245

* Creates a customized ref with explicit control over tracking and triggering

246

* @param factory - Factory function that receives track and trigger callbacks

247

* @returns A custom ref object

248

*/

249

function customRef<T>(factory: CustomRefFactory<T>): Ref<T>;

250

251

type CustomRefFactory<T> = (

252

track: () => void,

253

trigger: () => void

254

) => {

255

get: () => T;

256

set: (value: T) => void;

257

};

258

```

259

260

**Usage Examples:**

261

262

```typescript

263

import { customRef, effect } from "@vue/reactivity";

264

265

// Debounced ref that delays updates

266

function useDebouncedRef<T>(value: T, delay = 200) {

267

let timeout: number;

268

269

return customRef<T>((track, trigger) => {

270

return {

271

get() {

272

track(); // Track dependency

273

return value;

274

},

275

set(newValue: T) {

276

clearTimeout(timeout);

277

timeout = setTimeout(() => {

278

value = newValue;

279

trigger(); // Trigger updates after delay

280

}, delay);

281

}

282

};

283

});

284

}

285

286

const debouncedValue = useDebouncedRef("initial", 300);

287

288

effect(() => {

289

console.log("Debounced:", debouncedValue.value);

290

});

291

// Immediately logs: "Debounced: initial"

292

293

debouncedValue.value = "change1";

294

debouncedValue.value = "change2";

295

debouncedValue.value = "final";

296

// After 300ms delay, logs: "Debounced: final"

297

298

// Validated ref that only accepts valid values

299

function useValidatedRef<T>(

300

initialValue: T,

301

validator: (value: T) => boolean

302

) {

303

let _value = initialValue;

304

305

return customRef<T>((track, trigger) => {

306

return {

307

get() {

308

track();

309

return _value;

310

},

311

set(newValue: T) {

312

if (validator(newValue)) {

313

_value = newValue;

314

trigger();

315

} else {

316

console.warn("Invalid value rejected:", newValue);

317

}

318

}

319

};

320

});

321

}

322

323

const positiveNumber = useValidatedRef(1, (value: number) => value > 0);

324

325

effect(() => {

326

console.log("Valid number:", positiveNumber.value);

327

});

328

// Logs: "Valid number: 1"

329

330

positiveNumber.value = 5; // Logs: "Valid number: 5"

331

positiveNumber.value = -1; // Warns and doesn't update

332

positiveNumber.value = 10; // Logs: "Valid number: 10"

333

334

// Async ref that tracks loading state

335

function useAsyncRef<T>(asyncFn: () => Promise<T>, initialValue: T) {

336

let _value = initialValue;

337

let _loading = false;

338

let _error: Error | null = null;

339

340

const valueRef = customRef<T>((track, trigger) => ({

341

get() {

342

track();

343

return _value;

344

},

345

set(newValue: T) {

346

_value = newValue;

347

trigger();

348

}

349

}));

350

351

const loadingRef = customRef<boolean>((track, trigger) => ({

352

get() {

353

track();

354

return _loading;

355

},

356

set(newValue: boolean) {

357

_loading = newValue;

358

trigger();

359

}

360

}));

361

362

const load = async () => {

363

loadingRef.value = true;

364

_error = null;

365

366

try {

367

const result = await asyncFn();

368

valueRef.value = result;

369

} catch (error) {

370

_error = error as Error;

371

} finally {

372

loadingRef.value = false;

373

}

374

};

375

376

return {

377

value: valueRef,

378

loading: loadingRef,

379

error: () => _error,

380

load

381

};

382

}

383

```

384

385

### proxyRefs()

386

387

Returns a proxy that shallowly unwraps properties that are refs, allowing direct property access without `.value`.

388

389

```typescript { .api }

390

/**

391

* Creates a proxy that automatically unwraps ref properties

392

* @param objectWithRefs - Object containing ref properties

393

* @returns Proxy with automatic ref unwrapping

394

*/

395

function proxyRefs<T extends object>(objectWithRefs: T): ShallowUnwrapRef<T>;

396

397

type ShallowUnwrapRef<T> = {

398

[K in keyof T]: DistributeRef<T[K]>;

399

};

400

```

401

402

**Usage Examples:**

403

404

```typescript

405

import { ref, computed, proxyRefs, effect } from "@vue/reactivity";

406

407

// Object with mixed refs and plain values

408

const refs = {

409

count: ref(0),

410

message: ref("Hello"),

411

doubled: computed(() => refs.count.value * 2),

412

config: { theme: "dark" }, // Plain object

413

items: ref([1, 2, 3])

414

};

415

416

// Create proxy that unwraps refs

417

const proxy = proxyRefs(refs);

418

419

// Access without .value

420

console.log(proxy.count); // 0 (unwrapped from ref)

421

console.log(proxy.message); // "Hello" (unwrapped from ref)

422

console.log(proxy.doubled); // 0 (unwrapped from computed)

423

console.log(proxy.config); // { theme: "dark" } (plain value)

424

425

// Assignment unwraps refs automatically

426

proxy.count = 5; // Same as refs.count.value = 5

427

proxy.message = "Hi"; // Same as refs.message.value = "Hi"

428

429

// Effects work with the proxy

430

effect(() => {

431

console.log(`${proxy.message}: ${proxy.count} (doubled: ${proxy.doubled})`);

432

});

433

// Logs: "Hi: 5 (doubled: 10)"

434

435

proxy.count = 10; // Logs: "Hi: 10 (doubled: 20)"

436

437

// Useful for component setup return

438

function useComponent() {

439

const state = reactive({

440

loading: false,

441

data: null

442

});

443

444

const count = ref(0);

445

const doubled = computed(() => count.value * 2);

446

447

// Return proxy for easy template access

448

return proxyRefs({

449

...toRefs(state),

450

count,

451

doubled,

452

increment: () => count.value++

453

});

454

}

455

456

const component = useComponent();

457

// Template can use: component.loading, component.count, etc.

458

```

459

460

### Type Checking and Conversion Utilities

461

462

Additional utilities for working with reactive types and checking proxy states:

463

464

```typescript { .api }

465

/**

466

* Checks if a value is a proxy created by reactive(), readonly(), shallowReactive() or shallowReadonly()

467

* @param value - Value to check

468

* @returns True if the value is a proxy

469

*/

470

function isProxy(value: any): boolean;

471

472

/**

473

* Checks if a value is a shallow reactive proxy

474

* @param value - Value to check

475

* @returns True if the value is shallow

476

*/

477

function isShallow(value: unknown): boolean;

478

479

/**

480

* Returns a reactive proxy if the value is an object, otherwise the value itself

481

* @param value - Value to potentially make reactive

482

* @returns Reactive proxy or original value

483

*/

484

function toReactive<T>(value: T): T;

485

486

/**

487

* Returns a readonly proxy if the value is an object, otherwise the value itself

488

* @param value - Value to potentially make readonly

489

* @returns Readonly proxy or original value

490

*/

491

function toReadonly<T>(value: T): T;

492

```

493

494

**Usage Examples:**

495

496

```typescript

497

import {

498

reactive, readonly, shallowReactive, shallowReadonly,

499

isProxy, isShallow, isReactive, isReadonly,

500

toReactive, toReadonly, markRaw

501

} from "@vue/reactivity";

502

503

// Type checking utilities

504

const plainObj = { count: 0 };

505

const reactiveObj = reactive(plainObj);

506

const readonlyObj = readonly(plainObj);

507

const shallowObj = shallowReactive(plainObj);

508

const rawObj = markRaw({ data: [] });

509

510

console.log(isProxy(plainObj)); // false

511

console.log(isProxy(reactiveObj)); // true

512

console.log(isProxy(readonlyObj)); // true

513

console.log(isProxy(shallowObj)); // true

514

console.log(isProxy(rawObj)); // false

515

516

console.log(isShallow(reactiveObj)); // false (deep reactive)

517

console.log(isShallow(shallowObj)); // true

518

console.log(isShallow(readonlyObj)); // false

519

520

// toReactive converts objects to reactive, leaves primitives alone

521

const reactiveConverted = toReactive({ count: 0 }); // Becomes reactive

522

const reactiveNum = toReactive(42); // Stays 42

523

const reactiveStr = toReactive("hello"); // Stays "hello"

524

525

console.log(isReactive(reactiveConverted)); // true

526

console.log(isReactive(reactiveNum)); // false

527

console.log(isReactive(reactiveStr)); // false

528

529

// toReadonly converts objects to readonly, leaves primitives alone

530

const readonlyConverted = toReadonly({ count: 0 }); // Becomes readonly

531

const readonlyNum = toReadonly(42); // Stays 42

532

533

console.log(isReadonly(readonlyConverted)); // true

534

console.log(isReadonly(readonlyNum)); // false

535

536

// Useful for conditional reactivity

537

function makeReactiveIf<T>(value: T, condition: boolean): T {

538

return condition ? toReactive(value) : value;

539

}

540

541

const conditionallyReactive = makeReactiveIf({ data: [] }, true);

542

543

// Utility for checking proxy types

544

function getProxyType(value: any): string {

545

if (!isProxy(value)) return 'not-proxy';

546

if (isReadonly(value)) return isShallow(value) ? 'shallow-readonly' : 'readonly';

547

if (isReactive(value)) return isShallow(value) ? 'shallow-reactive' : 'reactive';

548

return 'unknown-proxy';

549

}

550

551

console.log(getProxyType(plainObj)); // 'not-proxy'

552

console.log(getProxyType(reactiveObj)); // 'reactive'

553

console.log(getProxyType(readonlyObj)); // 'readonly'

554

console.log(getProxyType(shallowObj)); // 'shallow-reactive'

555

```

556

557

## Advanced Utility Patterns

558

559

### Flexible Parameter Handling

560

561

Create utilities that work with different input types:

562

563

```typescript

564

import { toValue, MaybeRefOrGetter } from "@vue/reactivity";

565

566

function useFormatter<T>(

567

getValue: MaybeRefOrGetter<T>,

568

formatter: (value: T) => string

569

) {

570

return computed(() => {

571

const value = toValue(getValue);

572

return formatter(value);

573

});

574

}

575

576

// Works with refs, getters, or plain values

577

const count = ref(42);

578

const formatted1 = useFormatter(count, (n) => `Count: ${n}`);

579

const formatted2 = useFormatter(() => count.value * 2, (n) => `Double: ${n}`);

580

const formatted3 = useFormatter(100, (n) => `Static: ${n}`);

581

```

582

583

### Reactive Object Composition

584

585

Combine utilities for complex reactive patterns:

586

587

```typescript

588

import { reactive, toRefs, markRaw, customRef } from "@vue/reactivity";

589

590

function useStore<T extends object>(initialData: T) {

591

// Core reactive state

592

const state = reactive({ ...initialData });

593

594

// Non-reactive metadata

595

const meta = markRaw({

596

created: new Date(),

597

version: "1.0.0"

598

});

599

600

// Custom refs for special behavior

601

const lastUpdated = customRef<Date>((track, trigger) => {

602

let value = new Date();

603

return {

604

get() {

605

track();

606

return value;

607

},

608

set() {

609

value = new Date();

610

trigger();

611

}

612

};

613

});

614

615

// Update function that triggers lastUpdated

616

const updateState = (updates: Partial<T>) => {

617

Object.assign(state, updates);

618

lastUpdated.value = new Date(); // Triggers the custom ref

619

};

620

621

return {

622

...toRefs(state), // Individual property refs

623

state, // Full reactive state

624

meta, // Non-reactive metadata

625

lastUpdated, // Custom ref

626

updateState // Update function

627

};

628

}

629

630

const store = useStore({

631

count: 0,

632

message: "Hello"

633

});

634

635

// Use individual refs

636

store.count.value = 5;

637

638

// Or update via function

639

store.updateState({ message: "Updated" });

640

641

console.log(store.lastUpdated.value); // Shows update time

642

```

643

644

## Types

645

646

```typescript { .api }

647

// Conversion types

648

type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>;

649

type ToRefs<T = any> = { [K in keyof T]: ToRef<T[K]> };

650

651

// Utility types

652

type MaybeRef<T = any> = T | Ref<T> | ShallowRef<T> | WritableComputedRef<T>;

653

type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T);

654

655

// Unwrapping types

656

type ShallowUnwrapRef<T> = { [K in keyof T]: DistributeRef<T[K]> };

657

type DistributeRef<T> = T extends Ref<infer V> ? V : T;

658

659

// Raw type

660

type Raw<T> = T & { [RawSymbol]?: true };

661

662

// Custom ref factory

663

type CustomRefFactory<T> = (

664

track: () => void,

665

trigger: () => void

666

) => {

667

get: () => T;

668

set: (value: T) => void;

669

};

670

671

// Conditional type helpers

672

type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;

673

```