or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compiler-options.mdindex.mdnode-analysis.mdsyntax-utilities.mdtype-system.mdusage-analysis.md

type-system.mddocs/

0

# Type System Utilities

1

2

## Overview

3

4

The Type System Utilities are the second largest collection of functions in ts-api-utils, providing comprehensive tools for analyzing, inspecting, and working with TypeScript's type system. These utilities are essential for building robust TypeScript tooling, linters, and code analysis tools that need to understand and manipulate type information.

5

6

TypeScript's type system is complex and hierarchical, with various type categories including intrinsic types (built-in primitives), literal types, object types, union/intersection types, and more specialized constructs. The type system utilities provide a systematic way to inspect and classify types, extract type information, and perform type-aware operations.

7

8

## Core Concepts

9

10

### Types

11

TypeScript represents types as instances of the `ts.Type` interface. Each type has flags (`ts.TypeFlags`) that indicate its category and characteristics. Types can be simple primitives like `string` or `number`, complex object types, or computed types like unions and intersections.

12

13

### Type Checking

14

The TypeScript compiler provides a `TypeChecker` that can resolve types at specific locations in the AST, compare types, and provide detailed type information. Many utilities work in conjunction with the type checker to provide accurate analysis.

15

16

### Type Guards

17

Type guards are functions that narrow the type of a value through runtime checks, allowing TypeScript to understand more specific type information in conditional blocks. The utilities provide numerous type guards for different type categories.

18

19

## Type Getters

20

21

Functions that extract or derive type information from TypeScript types.

22

23

### getCallSignaturesOfType

24

25

Get the call signatures of a given type, handling union and intersection types appropriately.

26

27

```typescript { .api }

28

function getCallSignaturesOfType(type: ts.Type): readonly ts.Signature[]

29

```

30

31

**Parameters:**

32

- `type: ts.Type` - The type to get call signatures from

33

34

**Returns:** Array of call signatures. For union types, returns all signatures from all union members. For intersection types, returns signatures only if exactly one member has signatures.

35

36

**Example:**

37

```typescript

38

declare const type: ts.Type;

39

40

const signatures = getCallSignaturesOfType(type);

41

for (const signature of signatures) {

42

// Process each call signature

43

}

44

```

45

46

### getPropertyOfType

47

48

Get a specific property symbol from a type by name, including support for well-known symbols.

49

50

```typescript { .api }

51

function getPropertyOfType(type: ts.Type, name: ts.__String): ts.Symbol | undefined

52

```

53

54

**Parameters:**

55

- `type: ts.Type` - The type to search for the property

56

- `name: ts.__String` - The escaped property name to look for

57

58

**Returns:** The symbol representing the property, or `undefined` if not found

59

60

**Example:**

61

```typescript

62

declare const property: ts.Symbol;

63

declare const type: ts.Type;

64

65

const symbol = getPropertyOfType(type, property.getEscapedName());

66

if (symbol) {

67

// Property exists on the type

68

}

69

```

70

71

### getWellKnownSymbolPropertyOfType

72

73

Retrieve a type symbol corresponding to a well-known symbol name (like `Symbol.iterator`).

74

75

```typescript { .api }

76

function getWellKnownSymbolPropertyOfType(

77

type: ts.Type,

78

wellKnownSymbolName: string,

79

typeChecker: ts.TypeChecker

80

): ts.Symbol | undefined

81

```

82

83

**Parameters:**

84

- `type: ts.Type` - The type to search in

85

- `wellKnownSymbolName: string` - The well-known symbol name (e.g., "asyncIterator", "iterator")

86

- `typeChecker: ts.TypeChecker` - The type checker for resolving symbols

87

88

**Returns:** The symbol for the well-known symbol property, or `undefined` if not found

89

90

**Example:**

91

```typescript

92

declare const type: ts.Type;

93

declare const typeChecker: ts.TypeChecker;

94

95

const asyncIteratorSymbol = getWellKnownSymbolPropertyOfType(

96

type,

97

"asyncIterator",

98

typeChecker

99

);

100

```

101

102

## Type Guards

103

104

### Intrinsic Type Guards

105

106

Functions that check for TypeScript's built-in intrinsic types.

107

108

#### Type Definitions

109

110

```typescript { .api }

111

interface IntrinsicType extends ts.Type {

112

intrinsicName: string;

113

objectFlags: ts.ObjectFlags;

114

}

115

116

interface IntrinsicAnyType extends IntrinsicType {

117

intrinsicName: "any";

118

}

119

120

interface IntrinsicBigIntType extends IntrinsicType {

121

intrinsicName: "bigint";

122

}

123

124

interface IntrinsicBooleanType extends IntrinsicType {

125

intrinsicName: "boolean";

126

}

127

128

interface IntrinsicErrorType extends IntrinsicType {

129

intrinsicName: "error";

130

}

131

132

interface IntrinsicESSymbolType extends IntrinsicType {

133

intrinsicName: "symbol";

134

}

135

136

interface IntrinsicNeverType extends IntrinsicType {

137

intrinsicName: "never";

138

}

139

140

interface IntrinsicNonPrimitiveType extends IntrinsicType {

141

intrinsicName: "";

142

}

143

144

interface IntrinsicNullType extends IntrinsicType {

145

intrinsicName: "null";

146

}

147

148

interface IntrinsicNumberType extends IntrinsicType {

149

intrinsicName: "number";

150

}

151

152

interface IntrinsicStringType extends IntrinsicType {

153

intrinsicName: "string";

154

}

155

156

interface IntrinsicUndefinedType extends IntrinsicType {

157

intrinsicName: "undefined";

158

}

159

160

interface IntrinsicUnknownType extends IntrinsicType {

161

intrinsicName: "unknown";

162

}

163

164

interface IntrinsicVoidType extends IntrinsicType {

165

intrinsicName: "void";

166

}

167

```

168

169

#### isIntrinsicType

170

171

Test if a type is any intrinsic (built-in) type.

172

173

```typescript { .api }

174

function isIntrinsicType(type: ts.Type): type is IntrinsicType

175

```

176

177

#### isIntrinsicAnyType

178

179

Test if a type is the `any` intrinsic type.

180

181

```typescript { .api }

182

function isIntrinsicAnyType(type: ts.Type): type is IntrinsicAnyType

183

```

184

185

#### isIntrinsicBigIntType

186

187

Test if a type is the `bigint` intrinsic type.

188

189

```typescript { .api }

190

function isIntrinsicBigIntType(type: ts.Type): type is IntrinsicBigIntType

191

```

192

193

#### isIntrinsicBooleanType

194

195

Test if a type is the `boolean` intrinsic type.

196

197

```typescript { .api }

198

function isIntrinsicBooleanType(type: ts.Type): type is IntrinsicBooleanType

199

```

200

201

#### isIntrinsicErrorType

202

203

Test if a type is the `error` intrinsic type (occurs when TypeScript encounters resolution errors).

204

205

```typescript { .api }

206

function isIntrinsicErrorType(type: ts.Type): type is IntrinsicErrorType

207

```

208

209

#### isIntrinsicESSymbolType

210

211

Test if a type is the `symbol` intrinsic type.

212

213

```typescript { .api }

214

function isIntrinsicESSymbolType(type: ts.Type): type is IntrinsicESSymbolType

215

```

216

217

#### isIntrinsicNeverType

218

219

Test if a type is the `never` intrinsic type.

220

221

```typescript { .api }

222

function isIntrinsicNeverType(type: ts.Type): type is IntrinsicNeverType

223

```

224

225

#### isIntrinsicNonPrimitiveType

226

227

Test if a type is a non-primitive intrinsic type (e.g., `object`).

228

229

```typescript { .api }

230

function isIntrinsicNonPrimitiveType(type: ts.Type): type is IntrinsicNonPrimitiveType

231

```

232

233

#### isIntrinsicNullType

234

235

Test if a type is the `null` intrinsic type.

236

237

```typescript { .api }

238

function isIntrinsicNullType(type: ts.Type): type is IntrinsicNullType

239

```

240

241

#### isIntrinsicNumberType

242

243

Test if a type is the `number` intrinsic type.

244

245

```typescript { .api }

246

function isIntrinsicNumberType(type: ts.Type): type is IntrinsicNumberType

247

```

248

249

#### isIntrinsicStringType

250

251

Test if a type is the `string` intrinsic type.

252

253

```typescript { .api }

254

function isIntrinsicStringType(type: ts.Type): type is IntrinsicStringType

255

```

256

257

#### isIntrinsicUndefinedType

258

259

Test if a type is the `undefined` intrinsic type.

260

261

```typescript { .api }

262

function isIntrinsicUndefinedType(type: ts.Type): type is IntrinsicUndefinedType

263

```

264

265

#### isIntrinsicUnknownType

266

267

Test if a type is the `unknown` intrinsic type.

268

269

```typescript { .api }

270

function isIntrinsicUnknownType(type: ts.Type): type is IntrinsicUnknownType

271

```

272

273

#### isIntrinsicVoidType

274

275

Test if a type is the `void` intrinsic type.

276

277

```typescript { .api }

278

function isIntrinsicVoidType(type: ts.Type): type is IntrinsicVoidType

279

```

280

281

**Example:**

282

```typescript

283

declare const type: ts.Type;

284

285

if (isIntrinsicStringType(type)) {

286

// Type is the built-in string type

287

} else if (isIntrinsicNumberType(type)) {

288

// Type is the built-in number type

289

} else if (isIntrinsicErrorType(type)) {

290

// TypeScript encountered an error resolving this type

291

}

292

```

293

294

### Literal Type Guards

295

296

Functions that check for literal types (specific values like `"hello"` or `42`).

297

298

#### Type Definitions

299

300

```typescript { .api }

301

interface BooleanLiteralType extends FreshableIntrinsicType {

302

intrinsicName: "false" | "true";

303

}

304

305

interface FalseLiteralType extends BooleanLiteralType {

306

intrinsicName: "false";

307

}

308

309

interface TrueLiteralType extends BooleanLiteralType {

310

intrinsicName: "true";

311

}

312

313

interface UnknownLiteralType extends FreshableIntrinsicType {

314

value?: unknown;

315

}

316

```

317

318

#### isBigIntLiteralType

319

320

Test if a type is a bigint literal type (e.g., `123n`).

321

322

```typescript { .api }

323

function isBigIntLiteralType(type: ts.Type): type is ts.BigIntLiteralType

324

```

325

326

#### isBooleanLiteralType

327

328

Test if a type is a boolean literal type (`true` or `false`).

329

330

```typescript { .api }

331

function isBooleanLiteralType(type: ts.Type): type is BooleanLiteralType

332

```

333

334

#### isFalseLiteralType

335

336

Test if a type is the `false` literal type.

337

338

```typescript { .api }

339

function isFalseLiteralType(type: ts.Type): type is FalseLiteralType

340

```

341

342

#### isTrueLiteralType

343

344

Test if a type is the `true` literal type.

345

346

```typescript { .api }

347

function isTrueLiteralType(type: ts.Type): type is TrueLiteralType

348

```

349

350

#### isLiteralType

351

352

Test if a type is any kind of literal type.

353

354

```typescript { .api }

355

function isLiteralType(type: ts.Type): type is ts.LiteralType

356

```

357

358

#### isNumberLiteralType

359

360

Test if a type is a number literal type (e.g., `42`).

361

362

```typescript { .api }

363

function isNumberLiteralType(type: ts.Type): type is ts.NumberLiteralType

364

```

365

366

#### isStringLiteralType

367

368

Test if a type is a string literal type (e.g., `"hello"`).

369

370

```typescript { .api }

371

function isStringLiteralType(type: ts.Type): type is ts.StringLiteralType

372

```

373

374

#### isTemplateLiteralType

375

376

Test if a type is a template literal type (e.g., \`hello ${string}\`).

377

378

```typescript { .api }

379

function isTemplateLiteralType(type: ts.Type): type is ts.TemplateLiteralType

380

```

381

382

**Example:**

383

```typescript

384

declare const type: ts.Type;

385

386

if (isStringLiteralType(type)) {

387

console.log(`String literal: ${type.value}`);

388

} else if (isNumberLiteralType(type)) {

389

console.log(`Number literal: ${type.value}`);

390

} else if (isTrueLiteralType(type)) {

391

console.log('Boolean literal: true');

392

}

393

```

394

395

### Object Type Guards

396

397

Functions that check for object-related types.

398

399

#### isEvolvingArrayType

400

401

Test if a type is an evolving array type (used during type inference).

402

403

```typescript { .api }

404

function isEvolvingArrayType(type: ts.Type): type is ts.EvolvingArrayType

405

```

406

407

#### isTupleType

408

409

Test if a type is a tuple type.

410

411

```typescript { .api }

412

function isTupleType(type: ts.Type): type is ts.TupleType

413

```

414

415

#### isTypeReference

416

417

Test if a type is a type reference (generic type instantiation).

418

419

```typescript { .api }

420

function isTypeReference(type: ts.Type): type is ts.TypeReference

421

```

422

423

**Example:**

424

```typescript

425

declare const type: ts.Type;

426

427

if (isTupleType(type)) {

428

// Handle tuple type like [string, number]

429

} else if (isTypeReference(type)) {

430

// Handle generic type instantiation like Array<string>

431

}

432

```

433

434

### Single Type Guards

435

436

Functions that check for single, specific type categories.

437

438

#### isConditionalType

439

440

Test if a type is a conditional type (`T extends U ? X : Y`).

441

442

```typescript { .api }

443

function isConditionalType(type: ts.Type): type is ts.ConditionalType

444

```

445

446

#### isEnumType

447

448

Test if a type is an enum type.

449

450

```typescript { .api }

451

function isEnumType(type: ts.Type): type is ts.EnumType

452

```

453

454

#### isFreshableType

455

456

Test if a type is freshable (can have fresh literal types).

457

458

```typescript { .api }

459

function isFreshableType(type: ts.Type): type is ts.FreshableType

460

```

461

462

#### isIndexedAccessType

463

464

Test if a type is an indexed access type (`T[K]`).

465

466

```typescript { .api }

467

function isIndexedAccessType(type: ts.Type): type is ts.IndexedAccessType

468

```

469

470

#### isIndexType

471

472

Test if a type is an index type (`keyof T`).

473

474

```typescript { .api }

475

function isIndexType(type: ts.Type): type is ts.IndexType

476

```

477

478

#### isInstantiableType

479

480

Test if a type is instantiable (can be instantiated with type arguments).

481

482

```typescript { .api }

483

function isInstantiableType(type: ts.Type): type is ts.InstantiableType

484

```

485

486

#### isIntersectionType

487

488

Test if a type is an intersection type (`A & B`).

489

490

```typescript { .api }

491

function isIntersectionType(type: ts.Type): type is ts.IntersectionType

492

```

493

494

#### isObjectType

495

496

Test if a type is an object type.

497

498

```typescript { .api }

499

function isObjectType(type: ts.Type): type is ts.ObjectType

500

```

501

502

#### isStringMappingType

503

504

Test if a type is a string mapping type (like `Uppercase<T>`).

505

506

```typescript { .api }

507

function isStringMappingType(type: ts.Type): type is ts.StringMappingType

508

```

509

510

#### isSubstitutionType

511

512

Test if a type is a substitution type.

513

514

```typescript { .api }

515

function isSubstitutionType(type: ts.Type): type is ts.SubstitutionType

516

```

517

518

#### isTypeParameter

519

520

Test if a type is a type parameter. Note: This is intentionally not a type guard.

521

522

```typescript { .api }

523

function isTypeParameter(type: ts.Type): boolean

524

```

525

526

#### isTypeVariable

527

528

Test if a type is a type variable.

529

530

```typescript { .api }

531

function isTypeVariable(type: ts.Type): type is ts.TypeVariable

532

```

533

534

#### isUnionOrIntersectionType

535

536

Test if a type is either a union or intersection type.

537

538

```typescript { .api }

539

function isUnionOrIntersectionType(type: ts.Type): type is ts.UnionOrIntersectionType

540

```

541

542

#### isUnionType

543

544

Test if a type is a union type (`A | B`).

545

546

```typescript { .api }

547

function isUnionType(type: ts.Type): type is ts.UnionType

548

```

549

550

#### isUniqueESSymbolType

551

552

Test if a type is a unique ES symbol type.

553

554

```typescript { .api }

555

function isUniqueESSymbolType(type: ts.Type): type is ts.UniqueESSymbolType

556

```

557

558

### Compound Type Guards

559

560

Functions that check for types with multiple characteristics.

561

562

#### Type Definitions

563

564

```typescript { .api }

565

interface FreshableIntrinsicType extends ts.FreshableType, IntrinsicType {}

566

```

567

568

#### isFreshableIntrinsicType

569

570

Test if a type is both intrinsic and freshable.

571

572

```typescript { .api }

573

function isFreshableIntrinsicType(type: ts.Type): type is FreshableIntrinsicType

574

```

575

576

#### isTupleTypeReference

577

578

Test if a type is a tuple type reference (reference to a tuple type).

579

580

```typescript { .api }

581

function isTupleTypeReference(type: ts.Type): type is ts.TupleTypeReference

582

```

583

584

## Type Utilities

585

586

Helper functions for working with and manipulating types.

587

588

### intersectionTypeParts

589

590

Get the constituent types of an intersection type. If the type is not an intersection, returns an array containing only that type.

591

592

```typescript { .api }

593

function intersectionTypeParts(type: ts.Type): ts.Type[]

594

```

595

596

**Parameters:**

597

- `type: ts.Type` - The type to decompose

598

599

**Returns:** Array of constituent types

600

601

**Example:**

602

```typescript

603

declare const type: ts.Type;

604

605

for (const typePart of intersectionTypeParts(type)) {

606

// Process each part of the intersection (or the type itself)

607

}

608

```

609

610

### unionTypeParts

611

612

Get the constituent types of a union type. If the type is not a union, returns an array containing only that type.

613

614

```typescript { .api }

615

function unionTypeParts(type: ts.Type): ts.Type[]

616

```

617

618

**Parameters:**

619

- `type: ts.Type` - The type to decompose

620

621

**Returns:** Array of constituent types

622

623

**Example:**

624

```typescript

625

declare const type: ts.Type;

626

627

for (const typePart of unionTypeParts(type)) {

628

// Process each part of the union (or the type itself)

629

}

630

```

631

632

### typeParts

633

634

Get the constituent types of either a union or intersection type. Returns `type.types` for union/intersection types, or `[type]` for other types.

635

636

```typescript { .api }

637

function typeParts(type: ts.Type): ts.Type[]

638

```

639

640

**Parameters:**

641

- `type: ts.Type` - The type to decompose

642

643

**Returns:** Array of constituent types

644

645

### isFalsyType

646

647

Determine if a type is definitely falsy (but doesn't unwrap union types).

648

649

```typescript { .api }

650

function isFalsyType(type: ts.Type): boolean

651

```

652

653

**Parameters:**

654

- `type: ts.Type` - The type to check

655

656

**Returns:** `true` if the type is definitely falsy

657

658

**Example:**

659

```typescript

660

declare const type: ts.Type;

661

662

if (isFalsyType(type)) {

663

// Type is falsy (null, undefined, false, 0, "", etc.)

664

}

665

```

666

667

### isPropertyReadonlyInType

668

669

Determine whether writing to a specific property of a type is allowed.

670

671

```typescript { .api }

672

function isPropertyReadonlyInType(

673

type: ts.Type,

674

name: ts.__String,

675

typeChecker: ts.TypeChecker

676

): boolean

677

```

678

679

**Parameters:**

680

- `type: ts.Type` - The type containing the property

681

- `name: ts.__String` - The property name to check

682

- `typeChecker: ts.TypeChecker` - Type checker for analysis

683

684

**Returns:** `true` if the property is readonly

685

686

**Example:**

687

```typescript

688

declare const property: ts.Symbol;

689

declare const type: ts.Type;

690

declare const typeChecker: ts.TypeChecker;

691

692

if (isPropertyReadonlyInType(type, property.getEscapedName(), typeChecker)) {

693

// Property cannot be written to

694

}

695

```

696

697

### isThenableType

698

699

Determine if a type is thenable (has a `then` method) and can be used with `await`.

700

701

```typescript { .api }

702

function isThenableType(

703

typeChecker: ts.TypeChecker,

704

node: ts.Node,

705

type: ts.Type

706

): boolean

707

708

function isThenableType(

709

typeChecker: ts.TypeChecker,

710

node: ts.Expression,

711

type?: ts.Type

712

): boolean

713

```

714

715

**Parameters:**

716

- `typeChecker: ts.TypeChecker` - Type checker for analysis

717

- `node: ts.Node | ts.Expression` - The AST node for context

718

- `type?: ts.Type` - Optional type to check (inferred from node if not provided)

719

720

**Returns:** `true` if the type is thenable

721

722

**Example:**

723

```typescript

724

declare const node: ts.Node;

725

declare const type: ts.Type;

726

declare const typeChecker: ts.TypeChecker;

727

728

if (isThenableType(typeChecker, node, type)) {

729

// Type can be awaited

730

}

731

```

732

733

### symbolHasReadonlyDeclaration

734

735

Test if a symbol has any readonly declarations.

736

737

```typescript { .api }

738

function symbolHasReadonlyDeclaration(

739

symbol: ts.Symbol,

740

typeChecker: ts.TypeChecker

741

): boolean

742

```

743

744

**Parameters:**

745

- `symbol: ts.Symbol` - The symbol to check

746

- `typeChecker: ts.TypeChecker` - Type checker for analysis

747

748

**Returns:** `true` if the symbol has readonly declarations

749

750

**Example:**

751

```typescript

752

declare const symbol: ts.Symbol;

753

declare const typeChecker: ts.TypeChecker;

754

755

if (symbolHasReadonlyDeclaration(symbol, typeChecker)) {

756

// Symbol is declared as readonly

757

}

758

```

759

760

### typeIsLiteral

761

762

Test if a type is a literal type, with proper handling for bigint literals in older TypeScript versions.

763

764

```typescript { .api }

765

function typeIsLiteral(type: ts.Type): type is ts.LiteralType

766

```

767

768

**Parameters:**

769

- `type: ts.Type` - The type to check

770

771

**Returns:** `true` if the type is a literal type

772

773

**Note:** This function provides a compatibility layer for TypeScript versions before 5.0, where `type.isLiteral()` didn't correctly handle bigint literals.

774

775

**Example:**

776

```typescript

777

declare const type: ts.Type;

778

779

if (typeIsLiteral(type)) {

780

// Type is a literal (string, number, bigint, etc.)

781

}

782

```

783

784

## Usage Examples

785

786

### Analyzing Function Types

787

788

```typescript

789

function analyzeCallableType(type: ts.Type, typeChecker: ts.TypeChecker) {

790

const signatures = getCallSignaturesOfType(type);

791

792

if (signatures.length === 0) {

793

console.log('Type is not callable');

794

return;

795

}

796

797

console.log(`Type has ${signatures.length} call signature(s)`);

798

for (const signature of signatures) {

799

const params = signature.getParameters();

800

console.log(` ${params.length} parameter(s)`);

801

}

802

}

803

```

804

805

### Type Classification

806

807

```typescript

808

function classifyType(type: ts.Type): string {

809

if (isIntrinsicType(type)) {

810

return `Intrinsic: ${type.intrinsicName || 'unknown'}`;

811

}

812

813

if (isLiteralType(type)) {

814

if (isStringLiteralType(type)) {

815

return `String literal: "${type.value}"`;

816

} else if (isNumberLiteralType(type)) {

817

return `Number literal: ${type.value}`;

818

} else if (isBooleanLiteralType(type)) {

819

return `Boolean literal: ${type.intrinsicName}`;

820

}

821

return 'Literal type';

822

}

823

824

if (isUnionType(type)) {

825

const parts = unionTypeParts(type);

826

return `Union of ${parts.length} types`;

827

}

828

829

if (isIntersectionType(type)) {

830

const parts = intersectionTypeParts(type);

831

return `Intersection of ${parts.length} types`;

832

}

833

834

return 'Other type';

835

}

836

```

837

838

### Property Analysis

839

840

```typescript

841

function analyzeProperty(

842

type: ts.Type,

843

propertyName: string,

844

typeChecker: ts.TypeChecker

845

) {

846

const escapedName = propertyName as ts.__String;

847

const property = getPropertyOfType(type, escapedName);

848

849

if (!property) {

850

console.log(`Property '${propertyName}' not found`);

851

return;

852

}

853

854

const isReadonly = isPropertyReadonlyInType(type, escapedName, typeChecker);

855

console.log(`Property '${propertyName}' is ${isReadonly ? 'readonly' : 'mutable'}`);

856

857

if (symbolHasReadonlyDeclaration(property, typeChecker)) {

858

console.log('Property has readonly declaration');

859

}

860

}

861

```

862

863

The Type System Utilities provide the foundational tools needed for sophisticated TypeScript analysis, enabling developers to build tools that understand and work with TypeScript's rich type system in a reliable and comprehensive way.