or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

builtin-types.mdindex.mdproperty-types.mdsymbol-analysis.mdtype-analysis.mdtype-constraints.mdtype-predicates.mdtype-safety.mdtype-specifiers.md

type-specifiers.mddocs/

0

# Type Specifiers

1

2

Advanced type matching system using specifiers to identify types from specific packages, files, or the TypeScript standard library. This system provides flexible type identification beyond simple name matching.

3

4

## Capabilities

5

6

### Type Specifier Definitions

7

8

Core types and interfaces for the type specifier system.

9

10

```typescript { .api }

11

interface FileSpecifier {

12

from: 'file';

13

name: string | string[];

14

path?: string;

15

}

16

17

interface LibSpecifier {

18

from: 'lib';

19

name: string | string[];

20

}

21

22

interface PackageSpecifier {

23

from: 'package';

24

name: string | string[];

25

package: string;

26

}

27

28

type TypeOrValueSpecifier = string | FileSpecifier | LibSpecifier | PackageSpecifier;

29

```

30

31

**Constants:**

32

33

```typescript { .api }

34

const typeOrValueSpecifiersSchema: JSONSchema4;

35

```

36

37

### Type Matching Functions

38

39

Functions for matching types against specifiers.

40

41

```typescript { .api }

42

/**

43

* Checks if a type matches a specific TypeOrValueSpecifier

44

*/

45

function typeMatchesSpecifier(

46

type: ts.Type,

47

specifier: TypeOrValueSpecifier,

48

program: ts.Program

49

): boolean;

50

51

/**

52

* Checks if a type matches any of the provided specifiers

53

*/

54

function typeMatchesSomeSpecifier(

55

type: ts.Type,

56

specifiers: TypeOrValueSpecifier[],

57

program: ts.Program

58

): boolean;

59

```

60

61

### Value Matching Functions

62

63

Functions for matching ESTree nodes against specifiers.

64

65

```typescript { .api }

66

/**

67

* Checks if a value node matches a specific TypeOrValueSpecifier

68

*/

69

function valueMatchesSpecifier(

70

node: TSESTree.Node,

71

specifier: TypeOrValueSpecifier,

72

program: ts.Program,

73

type: ts.Type

74

): boolean;

75

76

/**

77

* Checks if a value node matches any of the provided specifiers

78

*/

79

function valueMatchesSomeSpecifier(

80

node: TSESTree.Node,

81

specifiers: TypeOrValueSpecifier[],

82

program: ts.Program,

83

type: ts.Type

84

): boolean;

85

```

86

87

## Usage Examples

88

89

### Basic Type Specifier Usage

90

91

```typescript

92

import {

93

typeMatchesSpecifier,

94

typeMatchesSomeSpecifier,

95

TypeOrValueSpecifier

96

} from "@typescript-eslint/type-utils";

97

98

// In an ESLint rule

99

export default {

100

create(context) {

101

const services = context.parserServices;

102

const program = services.program;

103

const checker = program.getTypeChecker();

104

105

// Define allowed types using specifiers

106

const allowedTypes: TypeOrValueSpecifier[] = [

107

// Simple string name

108

"string",

109

110

// Built-in lib types

111

{ from: "lib", name: "Promise" },

112

{ from: "lib", name: ["Array", "ReadonlyArray"] },

113

114

// Package types

115

{ from: "package", name: "Observable", package: "rxjs" },

116

{ from: "package", name: ["Component", "Injectable"], package: "@angular/core" },

117

118

// File-specific types

119

{ from: "file", name: "UserType", path: "./types/user.ts" },

120

{ from: "file", name: "ApiResponse" } // Any file

121

];

122

123

return {

124

VariableDeclarator(node) {

125

if (node.init) {

126

const tsNode = services.esTreeNodeToTSNodeMap.get(node.init);

127

const type = checker.getTypeAtLocation(tsNode);

128

129

// Check if type matches any allowed specifier

130

if (!typeMatchesSomeSpecifier(type, allowedTypes, program)) {

131

context.report({

132

node: node.init,

133

messageId: "disallowedType",

134

data: {

135

typeName: checker.typeToString(type)

136

}

137

});

138

}

139

}

140

}

141

};

142

}

143

};

144

```

145

146

### Package-Specific Type Checking

147

148

```typescript

149

import { typeMatchesSpecifier, PackageSpecifier } from "@typescript-eslint/type-utils";

150

151

// Check for specific React types

152

const reactSpecifiers: PackageSpecifier[] = [

153

{ from: "package", name: "Component", package: "react" },

154

{ from: "package", name: "FC", package: "react" },

155

{ from: "package", name: "ReactNode", package: "react" }

156

];

157

158

export default {

159

create(context) {

160

const services = context.parserServices;

161

const program = services.program;

162

const checker = program.getTypeChecker();

163

164

return {

165

ClassDeclaration(node) {

166

if (node.superClass) {

167

const tsNode = services.esTreeNodeToTSNodeMap.get(node.superClass);

168

const type = checker.getTypeAtLocation(tsNode);

169

170

const isReactComponent = reactSpecifiers.some(spec =>

171

typeMatchesSpecifier(type, spec, program)

172

);

173

174

if (isReactComponent) {

175

// This class extends a React component

176

console.log("React component detected");

177

}

178

}

179

}

180

};

181

}

182

};

183

```

184

185

### File-Based Type Restrictions

186

187

```typescript

188

import { typeMatchesSpecifier, FileSpecifier } from "@typescript-eslint/type-utils";

189

190

// Restrict types to specific files

191

const internalTypeSpecifiers: FileSpecifier[] = [

192

{ from: "file", name: ["InternalAPI", "PrivateType"], path: "./internal" },

193

{ from: "file", name: "ConfigType", path: "./config/types.ts" }

194

];

195

196

export default {

197

create(context) {

198

const services = context.parserServices;

199

const program = services.program;

200

const checker = program.getTypeChecker();

201

202

return {

203

TSTypeReference(node) {

204

const tsNode = services.esTreeNodeToTSNodeMap.get(node);

205

const type = checker.getTypeAtLocation(tsNode);

206

207

const isInternalType = internalTypeSpecifiers.some(spec =>

208

typeMatchesSpecifier(type, spec, program)

209

);

210

211

if (isInternalType) {

212

// Check if this usage is in an appropriate location

213

const sourceFile = node.getSourceFile?.() || context.getSourceCode().ast;

214

const fileName = sourceFile.filename || "";

215

216

if (!fileName.includes("internal")) {

217

context.report({

218

node,

219

messageId: "internalTypeInPublicAPI"

220

});

221

}

222

}

223

}

224

};

225

}

226

};

227

```

228

229

### Value Specifier Usage

230

231

```typescript

232

import {

233

valueMatchesSpecifier,

234

valueMatchesSomeSpecifier,

235

TypeOrValueSpecifier

236

} from "@typescript-eslint/type-utils";

237

238

// Check values (not just types)

239

const dangerousFunctions: TypeOrValueSpecifier[] = [

240

{ from: "lib", name: "eval" },

241

{ from: "package", name: "exec", package: "child_process" },

242

{ from: "file", name: "unsafeOperation", path: "./unsafe-utils.ts" }

243

];

244

245

export default {

246

create(context) {

247

const services = context.parserServices;

248

const program = services.program;

249

const checker = program.getTypeChecker();

250

251

return {

252

CallExpression(node) {

253

const tsNode = services.esTreeNodeToTSNodeMap.get(node.callee);

254

const type = checker.getTypeAtLocation(tsNode);

255

256

if (valueMatchesSomeSpecifier(node.callee, dangerousFunctions, program, type)) {

257

context.report({

258

node: node.callee,

259

messageId: "dangerousFunctionCall"

260

});

261

}

262

}

263

};

264

}

265

};

266

```

267

268

## Advanced Specifier Patterns

269

270

### Dynamic Specifier Generation

271

272

```typescript

273

// Example: Creating specifiers based on configuration

274

import { TypeOrValueSpecifier } from "@typescript-eslint/type-utils";

275

276

interface AllowedTypeConfig {

277

packageName: string;

278

allowedTypes: string[];

279

allowedFiles?: string[];

280

}

281

282

function createSpecifiersFromConfig(configs: AllowedTypeConfig[]): TypeOrValueSpecifier[] {

283

const specifiers: TypeOrValueSpecifier[] = [];

284

285

configs.forEach(config => {

286

// Add package specifiers

287

config.allowedTypes.forEach(typeName => {

288

specifiers.push({

289

from: "package",

290

name: typeName,

291

package: config.packageName

292

});

293

});

294

295

// Add file specifiers if specified

296

config.allowedFiles?.forEach(filePath => {

297

config.allowedTypes.forEach(typeName => {

298

specifiers.push({

299

from: "file",

300

name: typeName,

301

path: filePath

302

});

303

});

304

});

305

});

306

307

return specifiers;

308

}

309

310

// Usage

311

const typeConfigs: AllowedTypeConfig[] = [

312

{

313

packageName: "lodash",

314

allowedTypes: ["Dictionary", "List"],

315

allowedFiles: ["./types/lodash.d.ts"]

316

},

317

{

318

packageName: "rxjs",

319

allowedTypes: ["Observable", "Subject", "BehaviorSubject"]

320

}

321

];

322

323

const allowedSpecifiers = createSpecifiersFromConfig(typeConfigs);

324

```

325

326

### Conditional Type Matching

327

328

```typescript

329

// Example: Conditional type matching based on context

330

import { typeMatchesSpecifier, TypeOrValueSpecifier } from "@typescript-eslint/type-utils";

331

332

function createContextualTypeChecker(

333

baseSpecifiers: TypeOrValueSpecifier[],

334

contextRules: Map<string, TypeOrValueSpecifier[]>

335

) {

336

return function checkTypeInContext(

337

type: ts.Type,

338

program: ts.Program,

339

context: string

340

): boolean {

341

// Check base allowed types first

342

const baseAllowed = baseSpecifiers.some(spec =>

343

typeMatchesSpecifier(type, spec, program)

344

);

345

346

if (baseAllowed) return true;

347

348

// Check context-specific rules

349

const contextSpecifiers = contextRules.get(context);

350

if (contextSpecifiers) {

351

return contextSpecifiers.some(spec =>

352

typeMatchesSpecifier(type, spec, program)

353

);

354

}

355

356

return false;

357

};

358

}

359

360

// Usage

361

const baseTypes: TypeOrValueSpecifier[] = [

362

"string", "number", "boolean",

363

{ from: "lib", name: "Promise" }

364

];

365

366

const contextRules = new Map([

367

["test", [

368

{ from: "package", name: "jest", package: "@types/jest" },

369

{ from: "package", name: "TestingLibrary", package: "@testing-library/react" }

370

]],

371

["api", [

372

{ from: "package", name: "Request", package: "express" },

373

{ from: "package", name: "Response", package: "express" }

374

]]

375

]);

376

377

const typeChecker = createContextualTypeChecker(baseTypes, contextRules);

378

```

379

380

### Hierarchical Type Checking

381

382

```typescript

383

// Example: Hierarchical type matching with inheritance

384

import { typeMatchesSpecifier, LibSpecifier } from "@typescript-eslint/type-utils";

385

386

interface TypeHierarchy {

387

base: TypeOrValueSpecifier[];

388

derived: Map<string, TypeOrValueSpecifier[]>;

389

}

390

391

function checkTypeHierarchy(

392

type: ts.Type,

393

program: ts.Program,

394

hierarchy: TypeHierarchy,

395

checker: ts.TypeChecker

396

): { matches: boolean; level: string } {

397

// Check base level first

398

const baseMatches = hierarchy.base.some(spec =>

399

typeMatchesSpecifier(type, spec, program)

400

);

401

402

if (baseMatches) {

403

return { matches: true, level: "base" };

404

}

405

406

// Check derived levels

407

for (const [level, specifiers] of hierarchy.derived) {

408

const derivedMatches = specifiers.some(spec =>

409

typeMatchesSpecifier(type, spec, program)

410

);

411

412

if (derivedMatches) {

413

return { matches: true, level };

414

}

415

}

416

417

return { matches: false, level: "none" };

418

}

419

420

// Usage for React component hierarchy

421

const reactHierarchy: TypeHierarchy = {

422

base: [

423

{ from: "lib", name: "HTMLElement" },

424

{ from: "package", name: "ReactNode", package: "react" }

425

],

426

derived: new Map([

427

["component", [

428

{ from: "package", name: "Component", package: "react" },

429

{ from: "package", name: "PureComponent", package: "react" }

430

]],

431

["functional", [

432

{ from: "package", name: "FC", package: "react" },

433

{ from: "package", name: "FunctionComponent", package: "react" }

434

]]

435

])

436

};

437

```