Skip to content
489 changes: 466 additions & 23 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,15 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
description: Diagnostics.Built_in_iterators_are_instantiated_with_a_TReturn_type_of_undefined_instead_of_any,
defaultValueDescription: Diagnostics.false_unless_strict_is_set,
},
{
name: "stableTypeOrdering",
type: "boolean",
affectsSemanticDiagnostics: true,
affectsBuildInfo: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Ensure_types_are_ordered_stably_and_deterministically_across_compilations,
defaultValueDescription: true,
Comment thread
jakebailey marked this conversation as resolved.
Outdated
},
{
name: "noImplicitThis",
type: "boolean",
Expand Down
10 changes: 6 additions & 4 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,7 @@ export function binarySearchKey<T, U>(array: readonly T[], key: U, keySelector:
while (low <= high) {
const middle = low + ((high - low) >> 1);
const midKey = keySelector(array[middle], middle);
switch (keyComparer(midKey, key)) {
switch (Math.sign(keyComparer(midKey, key))) {
case Comparison.LessThan:
low = middle + 1;
break;
Expand Down Expand Up @@ -1967,9 +1967,11 @@ export function equateStringsCaseSensitive(a: string, b: string): boolean {
return equateValues(a, b);
}

function compareComparableValues(a: string | undefined, b: string | undefined): Comparison;
function compareComparableValues(a: number | undefined, b: number | undefined): Comparison;
function compareComparableValues(a: string | number | undefined, b: string | number | undefined) {
/** @internal */
export function compareComparableValues(a: string | undefined, b: string | undefined): Comparison;
/** @internal */
export function compareComparableValues(a: number | undefined, b: number | undefined): Comparison;
export function compareComparableValues(a: string | number | undefined, b: string | number | undefined) {
return a === b ? Comparison.EqualTo :
a === undefined ? Comparison.LessThan :
b === undefined ? Comparison.GreaterThan :
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -6589,6 +6589,10 @@
"category": "Message",
"code": 6808
},
"Ensure types are ordered stably and deterministically across compilations.": {
"category": "Message",
"code": 6809
},

"one of:": {
"category": "Message",
Expand Down
74 changes: 42 additions & 32 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6320,42 +6320,49 @@ export interface SerializedTypeEntry {
trackedSymbols: readonly TrackedSymbol[] | undefined;
}

// Note that for types of different kinds, the numeric values of TypeFlags determine the order
// computed by the CompareTypes function and therefore the order of constituent types in union types.
// Since union type processing often bails out early when a result is known, it is important to order
// TypeFlags in increasing order of potential type complexity. In particular, indexed access and
// conditional types should sort last as those types are potentially recursive and possibly infinite.

// dprint-ignore
export const enum TypeFlags {
Any = 1 << 0,
Unknown = 1 << 1,
String = 1 << 2,
Number = 1 << 3,
Boolean = 1 << 4,
Enum = 1 << 5, // Numeric computed enum member value
BigInt = 1 << 6,
StringLiteral = 1 << 7,
NumberLiteral = 1 << 8,
BooleanLiteral = 1 << 9,
EnumLiteral = 1 << 10, // Always combined with StringLiteral, NumberLiteral, or Union
BigIntLiteral = 1 << 11,
ESSymbol = 1 << 12, // Type of symbol primitive introduced in ES6
UniqueESSymbol = 1 << 13, // unique symbol
Void = 1 << 14,
Undefined = 1 << 15,
Null = 1 << 16,
Never = 1 << 17, // Never type
TypeParameter = 1 << 18, // Type parameter
Object = 1 << 19, // Object type
Union = 1 << 20, // Union (T | U)
Intersection = 1 << 21, // Intersection (T & U)
Index = 1 << 22, // keyof T
IndexedAccess = 1 << 23, // T[K]
Conditional = 1 << 24, // T extends U ? X : Y
Substitution = 1 << 25, // Type parameter substitution
NonPrimitive = 1 << 26, // intrinsic object type
TemplateLiteral = 1 << 27, // Template literal type
StringMapping = 1 << 28, // Uppercase/Lowercase type
Undefined = 1 << 2,
Null = 1 << 3,
Void = 1 << 4,
String = 1 << 5,
Number = 1 << 6,
BigInt = 1 << 7,
Boolean = 1 << 8,
ESSymbol = 1 << 9, // Type of symbol primitive introduced in ES6
StringLiteral = 1 << 10,
NumberLiteral = 1 << 11,
BigIntLiteral = 1 << 12,
BooleanLiteral = 1 << 13,
UniqueESSymbol = 1 << 14, // unique symbol
EnumLiteral = 1 << 15, // Always combined with StringLiteral, NumberLiteral, or Union
Enum = 1 << 16, // Numeric computed enum member value (must be right after EnumLiteral, see getSortOrderFlags)
NonPrimitive = 1 << 17, // intrinsic object type
Never = 1 << 18, // Never type
TypeParameter = 1 << 19, // Type parameter
Object = 1 << 20, // Object type
Index = 1 << 21, // keyof T
TemplateLiteral = 1 << 22, // Template literal type
StringMapping = 1 << 23, // Uppercase/Lowercase type
Substitution = 1 << 24, // Type parameter substitution
IndexedAccess = 1 << 25, // T[K]
Conditional = 1 << 26, // T extends U ? X : Y
Union = 1 << 27, // Union (T | U)
Intersection = 1 << 28, // Intersection (T & U)
/** @internal */
Reserved1 = 1 << 29, // Used by union/intersection type construction
/** @internal */
Reserved2 = 1 << 30, // Used by union/intersection type construction

/** @internal */
Reserved3 = 1 << 31,
/** @internal */
AnyOrUnknown = Any | Unknown,
/** @internal */
Expand Down Expand Up @@ -6539,14 +6546,16 @@ export const enum ObjectFlags {
PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType,
/** @internal */
InstantiatedMapped = Mapped | Instantiated,
// Object flags that uniquely identify the kind of ObjectType
/** @internal */
ObjectTypeKindMask = ClassOrInterface | Reference | Tuple | Anonymous | Mapped | ReverseMapped | EvolvingArray,


// Flags that require TypeFlags.Object
ContainsSpread = 1 << 21, // Object literal contains spread operation
ObjectRestType = 1 << 22, // Originates in object rest declaration
InstantiationExpressionType = 1 << 23, // Originates in instantiation expression

// Object flags that uniquely identify the kind of ObjectType
/** @internal */
ObjectTypeKindMask = ClassOrInterface | Reference | Tuple | Anonymous | Mapped | ReverseMapped | EvolvingArray | InstantiationExpressionType | SingleSignatureType,

/** @internal */
IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type
// Flags that require TypeFlags.Object and ObjectFlags.Reference
Expand Down Expand Up @@ -7532,6 +7541,7 @@ export interface CompilerOptions {
strictNullChecks?: boolean; // Always combine with strict property
strictPropertyInitialization?: boolean; // Always combine with strict property
strictBuiltinIteratorReturn?: boolean; // Always combine with strict property
stableTypeOrdering?: boolean;
stripInternal?: boolean;
/** @deprecated */
suppressExcessPropertyErrors?: boolean;
Expand Down
95 changes: 48 additions & 47 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6593,53 +6593,53 @@ declare namespace ts {
enum TypeFlags {
Any = 1,
Unknown = 2,
String = 4,
Number = 8,
Boolean = 16,
Enum = 32,
BigInt = 64,
StringLiteral = 128,
NumberLiteral = 256,
BooleanLiteral = 512,
EnumLiteral = 1024,
BigIntLiteral = 2048,
ESSymbol = 4096,
UniqueESSymbol = 8192,
Void = 16384,
Undefined = 32768,
Null = 65536,
Never = 131072,
TypeParameter = 262144,
Object = 524288,
Union = 1048576,
Intersection = 2097152,
Index = 4194304,
IndexedAccess = 8388608,
Conditional = 16777216,
Substitution = 33554432,
NonPrimitive = 67108864,
TemplateLiteral = 134217728,
StringMapping = 268435456,
Literal = 2944,
Unit = 109472,
Freshable = 2976,
StringOrNumberLiteral = 384,
PossiblyFalsy = 117724,
StringLike = 402653316,
NumberLike = 296,
BigIntLike = 2112,
BooleanLike = 528,
EnumLike = 1056,
ESSymbolLike = 12288,
VoidLike = 49152,
UnionOrIntersection = 3145728,
StructuredType = 3670016,
TypeVariable = 8650752,
InstantiableNonPrimitive = 58982400,
InstantiablePrimitive = 406847488,
Instantiable = 465829888,
StructuredOrInstantiable = 469499904,
Narrowable = 536624127,
Undefined = 4,
Null = 8,
Void = 16,
String = 32,
Number = 64,
BigInt = 128,
Boolean = 256,
ESSymbol = 512,
StringLiteral = 1024,
NumberLiteral = 2048,
BigIntLiteral = 4096,
BooleanLiteral = 8192,
UniqueESSymbol = 16384,
EnumLiteral = 32768,
Enum = 65536,
NonPrimitive = 131072,
Never = 262144,
TypeParameter = 524288,
Object = 1048576,
Index = 2097152,
TemplateLiteral = 4194304,
StringMapping = 8388608,
Substitution = 16777216,
IndexedAccess = 33554432,
Conditional = 67108864,
Union = 134217728,
Intersection = 268435456,
Literal = 15360,
Unit = 97292,
Freshable = 80896,
StringOrNumberLiteral = 3072,
PossiblyFalsy = 15868,
StringLike = 12583968,
NumberLike = 67648,
BigIntLike = 4224,
BooleanLike = 8448,
EnumLike = 98304,
ESSymbolLike = 16896,
VoidLike = 20,
UnionOrIntersection = 402653184,
StructuredType = 403701760,
TypeVariable = 34078720,
InstantiableNonPrimitive = 117964800,
InstantiablePrimitive = 14680064,
Instantiable = 132644864,
StructuredOrInstantiable = 536346624,
Narrowable = 536575971,
}
type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
interface Type {
Expand Down Expand Up @@ -7114,6 +7114,7 @@ declare namespace ts {
strictNullChecks?: boolean;
strictPropertyInitialization?: boolean;
strictBuiltinIteratorReturn?: boolean;
stableTypeOrdering?: boolean;
stripInternal?: boolean;
/** @deprecated */
suppressExcessPropertyErrors?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"stableTypeOrdering": true
}
}
5 changes: 5 additions & 0 deletions tests/baselines/reference/tsc/commandLine/help-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ Raise an error when a function parameter isn't read.
type: boolean
default: false

--stableTypeOrdering
Ensure types are ordered stably and deterministically across compilations.
type: boolean
default: true

--strict
Enable all strict type-checking options.
type: boolean
Expand Down