or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcomponent-utilities.mdcomputed.mddependency-injection.mdindex.mdlifecycle.mdreactive-state.mdtypes.mdwatchers.md

dependency-injection.mddocs/

0

# Dependency Injection

1

2

Provide/inject system for passing data down the component tree without explicit prop drilling. This enables ancestor components to serve as dependency providers for all their descendants, regardless of how deep the component hierarchy is.

3

4

## Capabilities

5

6

### Provide Function

7

8

Provides a value that can be injected by descendant components. Values are provided with a key and can be accessed by any descendant component.

9

10

```typescript { .api }

11

/**

12

* Provides a value for descendant components to inject

13

* @param key - Injection key (string or symbol)

14

* @param value - Value to provide

15

*/

16

function provide<T>(key: InjectionKey<T> | string, value: T): void;

17

18

interface InjectionKey<T> extends Symbol {}

19

```

20

21

**Usage Examples:**

22

23

```typescript

24

import { provide, reactive } from "@vue/composition-api";

25

26

// String-based key

27

export default {

28

setup() {

29

const theme = reactive({

30

primaryColor: "#007bff",

31

secondaryColor: "#6c757d",

32

darkMode: false,

33

});

34

35

provide("theme", theme);

36

37

const userSettings = reactive({

38

language: "en",

39

timezone: "UTC",

40

});

41

42

provide("userSettings", userSettings);

43

44

return {};

45

},

46

};

47

48

// Symbol-based key for type safety

49

import { InjectionKey } from "@vue/composition-api";

50

51

interface Theme {

52

primaryColor: string;

53

secondaryColor: string;

54

darkMode: boolean;

55

}

56

57

const ThemeKey: InjectionKey<Theme> = Symbol("theme");

58

59

export default {

60

setup() {

61

const theme = reactive({

62

primaryColor: "#007bff",

63

secondaryColor: "#6c757d",

64

darkMode: false,

65

});

66

67

provide(ThemeKey, theme);

68

69

return {};

70

},

71

};

72

```

73

74

### Inject Function

75

76

Injects a value provided by an ancestor component. Supports default values and type-safe injection keys.

77

78

```typescript { .api }

79

/**

80

* Injects a value provided by an ancestor component

81

* @param key - Injection key to look for

82

* @returns Injected value or undefined if not found

83

*/

84

function inject<T>(key: InjectionKey<T> | string): T | undefined;

85

86

/**

87

* Injects a value with a default fallback

88

* @param key - Injection key to look for

89

* @param defaultValue - Default value if injection key not found

90

* @returns Injected value or default value

91

*/

92

function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T;

93

94

/**

95

* Injects a value with a default factory function

96

* @param key - Injection key to look for

97

* @param defaultValue - Factory function for default value

98

* @param treatDefaultAsFactory - Whether to treat default as factory (true by default)

99

* @returns Injected value or result of default factory

100

*/

101

function inject<T>(

102

key: InjectionKey<T> | string,

103

defaultValue: () => T,

104

treatDefaultAsFactory: boolean

105

): T;

106

```

107

108

**Usage Examples:**

109

110

```typescript

111

import { inject } from "@vue/composition-api";

112

113

// Basic injection

114

export default {

115

setup() {

116

const theme = inject("theme");

117

118

if (theme) {

119

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

120

}

121

122

return {

123

theme,

124

};

125

},

126

};

127

128

// Injection with default value

129

export default {

130

setup() {

131

const theme = inject("theme", {

132

primaryColor: "#000000",

133

secondaryColor: "#ffffff",

134

darkMode: false,

135

});

136

137

return {

138

theme,

139

};

140

},

141

};

142

143

// Type-safe injection with symbol key

144

import { InjectionKey, inject } from "@vue/composition-api";

145

146

interface Theme {

147

primaryColor: string;

148

secondaryColor: string;

149

darkMode: boolean;

150

}

151

152

const ThemeKey: InjectionKey<Theme> = Symbol("theme");

153

154

export default {

155

setup() {

156

const theme = inject(ThemeKey); // TypeScript knows this is Theme | undefined

157

const safeTheme = inject(ThemeKey, {

158

primaryColor: "#000000",

159

secondaryColor: "#ffffff",

160

darkMode: false,

161

}); // TypeScript knows this is Theme

162

163

return {

164

theme,

165

safeTheme,

166

};

167

},

168

};

169

```

170

171

### Advanced Injection Patterns

172

173

Complex patterns for dependency injection including reactive providers, service injection, and contextual data.

174

175

**Reactive Provider Pattern:**

176

177

```typescript

178

import { provide, reactive, readonly } from "@vue/composition-api";

179

180

// Provider component

181

export default {

182

setup() {

183

const state = reactive({

184

user: null,

185

isAuthenticated: false,

186

permissions: [],

187

});

188

189

const actions = {

190

login: async (credentials: any) => {

191

// Login logic

192

state.user = await authenticate(credentials);

193

state.isAuthenticated = true;

194

state.permissions = await getUserPermissions(state.user.id);

195

},

196

logout: () => {

197

state.user = null;

198

state.isAuthenticated = false;

199

state.permissions = [];

200

},

201

};

202

203

// Provide readonly state and actions

204

provide("auth", {

205

state: readonly(state),

206

actions,

207

});

208

209

return {};

210

},

211

};

212

213

// Consumer component

214

import { inject, computed } from "@vue/composition-api";

215

216

export default {

217

setup() {

218

const auth = inject("auth");

219

220

const canEditPosts = computed(() => {

221

return auth?.state.permissions.includes("edit:posts") ?? false;

222

});

223

224

const handleLogin = async () => {

225

await auth?.actions.login({ username: "user", password: "pass" });

226

};

227

228

return {

229

auth,

230

canEditPosts,

231

handleLogin,

232

};

233

},

234

};

235

```

236

237

**Service Injection Pattern:**

238

239

```typescript { .api }

240

import { provide, inject, InjectionKey } from "@vue/composition-api";

241

242

// Service interface

243

interface ApiService {

244

get<T>(url: string): Promise<T>;

245

post<T>(url: string, data: any): Promise<T>;

246

put<T>(url: string, data: any): Promise<T>;

247

delete(url: string): Promise<void>;

248

}

249

250

// Service implementation

251

class HttpApiService implements ApiService {

252

private baseUrl: string;

253

254

constructor(baseUrl: string) {

255

this.baseUrl = baseUrl;

256

}

257

258

async get<T>(url: string): Promise<T> {

259

const response = await fetch(`${this.baseUrl}${url}`);

260

return response.json();

261

}

262

263

async post<T>(url: string, data: any): Promise<T> {

264

const response = await fetch(`${this.baseUrl}${url}`, {

265

method: "POST",

266

headers: { "Content-Type": "application/json" },

267

body: JSON.stringify(data),

268

});

269

return response.json();

270

}

271

272

async put<T>(url: string, data: any): Promise<T> {

273

const response = await fetch(`${this.baseUrl}${url}`, {

274

method: "PUT",

275

headers: { "Content-Type": "application/json" },

276

body: JSON.stringify(data),

277

});

278

return response.json();

279

}

280

281

async delete(url: string): Promise<void> {

282

await fetch(`${this.baseUrl}${url}`, { method: "DELETE" });

283

}

284

}

285

286

// Service key

287

const ApiServiceKey: InjectionKey<ApiService> = Symbol("apiService");

288

289

// Root component provides service

290

export default {

291

setup() {

292

const apiService = new HttpApiService("/api");

293

provide(ApiServiceKey, apiService);

294

295

return {};

296

},

297

};

298

299

// Component uses service

300

export default {

301

setup() {

302

const api = inject(ApiServiceKey);

303

304

const loadData = async () => {

305

if (api) {

306

const data = await api.get("/users");

307

console.log("Users:", data);

308

}

309

};

310

311

return {

312

loadData,

313

};

314

},

315

};

316

```

317

318

**Configuration Injection:**

319

320

```typescript { .api }

321

import { provide, inject, InjectionKey } from "@vue/composition-api";

322

323

interface AppConfig {

324

apiUrl: string;

325

version: string;

326

features: {

327

darkMode: boolean;

328

notifications: boolean;

329

analytics: boolean;

330

};

331

}

332

333

const ConfigKey: InjectionKey<AppConfig> = Symbol("config");

334

335

// App root provides configuration

336

export default {

337

setup() {

338

const config: AppConfig = {

339

apiUrl: process.env.VUE_APP_API_URL || "/api",

340

version: process.env.VUE_APP_VERSION || "1.0.0",

341

features: {

342

darkMode: true,

343

notifications: true,

344

analytics: process.env.NODE_ENV === "production",

345

},

346

};

347

348

provide(ConfigKey, config);

349

350

return {};

351

},

352

};

353

354

// Component uses configuration

355

export default {

356

setup() {

357

const config = inject(ConfigKey, {

358

apiUrl: "/api",

359

version: "unknown",

360

features: { darkMode: false, notifications: false, analytics: false },

361

});

362

363

return {

364

config,

365

};

366

},

367

};

368

```

369

370

### Nested Providers

371

372

Providers can override values at different levels of the component tree, allowing for contextual customization.

373

374

```typescript

375

import { provide, inject } from "@vue/composition-api";

376

377

// Root level provider

378

export const RootProvider = {

379

setup() {

380

provide("theme", "light");

381

provide("lang", "en");

382

383

return {};

384

},

385

};

386

387

// Section level provider (overrides theme)

388

export const DarkSection = {

389

setup() {

390

provide("theme", "dark"); // Overrides root theme

391

// lang is still "en" from root

392

393

return {};

394

},

395

};

396

397

// Component uses nearest provider

398

export const ThemedComponent = {

399

setup() {

400

const theme = inject("theme", "light"); // Gets "dark" from DarkSection

401

const lang = inject("lang", "en"); // Gets "en" from root

402

403

return {

404

theme,

405

lang,

406

};

407

},

408

};

409

```

410

411

### Injection with Composables

412

413

Creating reusable composables that encapsulate injection logic.

414

415

```typescript { .api }

416

import { inject, InjectionKey, computed } from "@vue/composition-api";

417

418

interface User {

419

id: string;

420

name: string;

421

email: string;

422

role: string;

423

}

424

425

const UserKey: InjectionKey<User | null> = Symbol("user");

426

427

// Composable for user injection

428

export function useUser() {

429

const user = inject(UserKey, null);

430

431

const isLoggedIn = computed(() => user !== null);

432

const isAdmin = computed(() => user?.role === "admin" ?? false);

433

const canEdit = computed(() => isAdmin.value || user?.role === "editor");

434

435

return {

436

user,

437

isLoggedIn,

438

isAdmin,

439

canEdit,

440

};

441

}

442

443

// Usage in component

444

export default {

445

setup() {

446

const { user, isLoggedIn, isAdmin, canEdit } = useUser();

447

448

return {

449

user,

450

isLoggedIn,

451

isAdmin,

452

canEdit,

453

};

454

},

455

};

456

```

457

458

## Types

459

460

```typescript { .api }

461

interface InjectionKey<T> extends Symbol {}

462

463

type InjectionConstraint = string | number | boolean | object | symbol;

464

465

interface ProvideOptions {

466

[key: string | symbol]: any;

467

}

468

469

interface InjectOptions {

470

from?: string | symbol;

471

default?: any;

472

}

473

474

type InjectKey = string | symbol;

475

```