Open-Source Wikis

/

TypeScript

/

Primitives

/

Type

microsoft/TypeScript

Type

The checker's view of a value. Types are independent of any one declaration — they describe the shape a value can have. The whole "TypeScript is a type system on JavaScript" story bottoms out at the Type interface.

Definition

In src/compiler/types.ts:

export interface Type {
  flags: TypeFlags;
  /* @internal */ id: TypeId;
  symbol: Symbol;
  pattern?: DestructuringPattern;
  aliasSymbol?: Symbol;
  aliasTypeArguments?: readonly Type[];
  /* @internal */ aliasTypeArgumentsContainsMarker?: boolean;
  /* @internal */ permissiveInstantiation?: Type;
  /* @internal */ restrictiveInstantiation?: Type;
  /* @internal */ uniqueLiteralFilledInstantiation?: Type;
  /* @internal */ immediateBaseConstraint?: Type;
  /* @internal */ widened?: Type;
}

TypeFlags distinguishes the kind:

  • Primitives: Any, Unknown, String, Number, BigInt, Boolean, EnumLiteral, StringLiteral, NumberLiteral, BooleanLiteral, BigIntLiteral, Symbol, UniqueESSymbol, Void, Undefined, Null, Never.
  • Compound: Object, Union, Intersection, Index, IndexedAccess, Conditional, Substitution, TemplateLiteral, StringMapping.
  • Markers: TypeParameter, Literal, Nullable, Singleton, Instantiable, StructuredOrInstantiable, Narrowable, IncludesMissingType.

Subtypes carry their own properties: ObjectType adds members and signatures; UnionType lists alternatives; ConditionalType carries checkType/extendsType/trueType/falseType; etc.

Construction

Types are not allocated directly. Always go through the checker's factories: getStringType(), getNumberLiteralType(value), getUnionType(types), createAnonymousType(symbol, members, signatures, ...), etc.

The checker flyweights structural types: two unions of the same alternatives produce the same Type instance, so equality is reference equality. This is what makes the assignability cache work.

Type flags vs. object flags

For Object types, an additional objectFlags: ObjectFlags field distinguishes:

  • Class, Interface — declared types.
  • Reference — instantiation of a generic type.
  • Tuple, Anonymous — structural types.
  • Mapped, EvolvingArray, JsxAttributes.
  • Internal markers like JSLiteral, FreshLiteral, ObjectLiteralPatternWithComputedProperties.

Resolved members

ResolvedType (a subtype of ObjectType) is what callers iterate over when asking "what are the members of this type?". It exposes:

  • members: SymbolTable
  • properties: Symbol[]
  • callSignatures: readonly Signature[]
  • constructSignatures: readonly Signature[]
  • indexInfos: readonly IndexInfo[]

getResolvedMembers(type) is the lazy on-demand resolver. The checker calls it the first time members of a type are asked for and caches the result on the type instance.

Type relationships

The checker's most-used algorithms operate on type pairs:

  • isTypeAssignableTo(source, target) — is source a subtype of target?
  • isTypeRelatedTo(source, target, relation) — generalisation; relation is one of assignableRelation, subtypeRelation, comparableRelation, identityRelation, enumRelation.
  • compareTypesIdentical(a, b) — strict identity check.

These descend through structural shape, recording which subtype/equality assumptions are active to handle recursion. The resulting "relation" maps are cached per checker instance so the same pair is never compared twice.

Generics

A TypeParameter is Type & { isThisType?: boolean; constraint?: Type; default?: Type; symbol: Symbol; ... }. Generic types are uninstantiated; the checker substitutes type parameters for concrete types via instantiateType(type, mapper).

A TypeReference (a kind of ObjectType) represents an instantiation: Array<string> is a TypeReference with target = Array and typeArguments = [string]. The reference's resolved members are computed lazily by substituting T → string into Array's declared members.

Conditional types

ConditionalType is one of the more involved kinds. It carries:

  • checkType: Type — what's on the left of extends.
  • extendsType: Type — what's on the right.
  • resolvedTrueType: Type — the branch when assignable.
  • resolvedFalseType: Type — the branch when not.
  • inferTypeParameters?: TypeParameter[]infer X placeholders.

When the checker instantiates a conditional type with concrete arguments, it tries the extends relation. If decidable, it picks one branch; if not, the conditional remains unresolved (a deferred conditional) until more is known.

Narrowing

Within a block, the same symbol can have different types at different positions: if (typeof x === "string") { x.toUpperCase(); }. The checker computes a flow type for each reference using the binder's flow graph. The flow walker calls into narrowType* helpers for each kind of guard (typeof, instanceof, equality, in, custom type predicates).

Widening / freshness

Object literals and array literals start out fresh — their type allows extra checks to flag obvious typos. When assigned to a variable without an explicit type, they're widened into a regular type. The widened field on Type caches the result.

Integration points

  • The checker is the only producer of Type.
  • Callers include the language service (hover, completions, signature help), declaration emit (synthesising .d.ts types), and the emitter (asking for resolved imports and resolved external modules via EmitResolver).
  • Some callers (refactors, code fixes) ask the checker for names of types via typeToString or for node representations via nodeBuilder.typeToTypeNode.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Type – TypeScript wiki | Factory