From 37bafa539ce60a47cac73c99576573cce31ad9e4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 23 Mar 2023 07:04:16 -0700 Subject: [PATCH] Exclude comparable relation from literal type relation optimization (#53419) --- src/compiler/checker.ts | 5 +- .../comparableRelationBidirectional.symbols | 75 +++++++++++++++++++ .../comparableRelationBidirectional.types | 72 ++++++++++++++++++ .../comparableRelationBidirectional.ts | 37 +++++++++ 4 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/comparableRelationBidirectional.symbols create mode 100644 tests/baselines/reference/comparableRelationBidirectional.types create mode 100644 tests/cases/compiler/comparableRelationBidirectional.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b1becfb478..844e832909c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20899,14 +20899,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (containsType(targetTypes, source)) { return Ternary.True; } - if (getObjectFlags(target) & ObjectFlags.PrimitiveUnion && !(source.flags & TypeFlags.EnumLiteral) && ( + if (relation !== comparableRelation && getObjectFlags(target) & ObjectFlags.PrimitiveUnion && !(source.flags & TypeFlags.EnumLiteral) && ( source.flags & (TypeFlags.StringLiteral | TypeFlags.BooleanLiteral | TypeFlags.BigIntLiteral) || (relation === subtypeRelation || relation === strictSubtypeRelation) && source.flags & TypeFlags.NumberLiteral)) { // When relating a literal type to a union of primitive types, we know the relation is false unless // the union contains the base primitive type or the literal type in one of its fresh/regular forms. // We exclude numeric literals for non-subtype relations because numeric literals are assignable to // numeric enum literals with the same value. Similarly, we exclude enum literal types because - // identically named enum types are related (see isEmumTypeRelatedTo). + // identically named enum types are related (see isEnumTypeRelatedTo). We exclude the comparable + // relation in entirety because it needs to be checked in both directions. const alternateForm = source === (source as StringLiteralType).regularType ? (source as StringLiteralType).freshType : (source as StringLiteralType).regularType; const primitive = source.flags & TypeFlags.StringLiteral ? stringType : source.flags & TypeFlags.NumberLiteral ? numberType : diff --git a/tests/baselines/reference/comparableRelationBidirectional.symbols b/tests/baselines/reference/comparableRelationBidirectional.symbols new file mode 100644 index 00000000000..9c40506bcf1 --- /dev/null +++ b/tests/baselines/reference/comparableRelationBidirectional.symbols @@ -0,0 +1,75 @@ +=== tests/cases/compiler/comparableRelationBidirectional.ts === +enum AutomationMode { +>AutomationMode : Symbol(AutomationMode, Decl(comparableRelationBidirectional.ts, 0, 0)) + + NONE = "", +>NONE : Symbol(AutomationMode.NONE, Decl(comparableRelationBidirectional.ts, 0, 21)) + + TIME = "time", +>TIME : Symbol(AutomationMode.TIME, Decl(comparableRelationBidirectional.ts, 1, 14)) + + SYSTEM = "system", +>SYSTEM : Symbol(AutomationMode.SYSTEM, Decl(comparableRelationBidirectional.ts, 2, 18)) + + LOCATION = "location", +>LOCATION : Symbol(AutomationMode.LOCATION, Decl(comparableRelationBidirectional.ts, 3, 22)) +} + +interface ThemePreset { +>ThemePreset : Symbol(ThemePreset, Decl(comparableRelationBidirectional.ts, 5, 1)) + + id: string; +>id : Symbol(ThemePreset.id, Decl(comparableRelationBidirectional.ts, 7, 23)) +} + +interface Automation { +>Automation : Symbol(Automation, Decl(comparableRelationBidirectional.ts, 9, 1)) + + mode: AutomationMode; +>mode : Symbol(Automation.mode, Decl(comparableRelationBidirectional.ts, 11, 22)) +>AutomationMode : Symbol(AutomationMode, Decl(comparableRelationBidirectional.ts, 0, 0)) +} + +interface UserSettings { +>UserSettings : Symbol(UserSettings, Decl(comparableRelationBidirectional.ts, 13, 1)) + + presets: ThemePreset[]; +>presets : Symbol(UserSettings.presets, Decl(comparableRelationBidirectional.ts, 15, 24)) +>ThemePreset : Symbol(ThemePreset, Decl(comparableRelationBidirectional.ts, 5, 1)) + + automation: Automation; +>automation : Symbol(UserSettings.automation, Decl(comparableRelationBidirectional.ts, 16, 27)) +>Automation : Symbol(Automation, Decl(comparableRelationBidirectional.ts, 9, 1)) +} + +interface ExtensionData { +>ExtensionData : Symbol(ExtensionData, Decl(comparableRelationBidirectional.ts, 18, 1)) + + settings: UserSettings; +>settings : Symbol(ExtensionData.settings, Decl(comparableRelationBidirectional.ts, 20, 25)) +>UserSettings : Symbol(UserSettings, Decl(comparableRelationBidirectional.ts, 13, 1)) +} + +export function getMockData(): ExtensionData { +>getMockData : Symbol(getMockData, Decl(comparableRelationBidirectional.ts, 22, 1)) +>ExtensionData : Symbol(ExtensionData, Decl(comparableRelationBidirectional.ts, 18, 1)) + + return { + settings: { +>settings : Symbol(settings, Decl(comparableRelationBidirectional.ts, 25, 12)) + + presets: [], +>presets : Symbol(presets, Decl(comparableRelationBidirectional.ts, 26, 19)) + + automation: { +>automation : Symbol(automation, Decl(comparableRelationBidirectional.ts, 27, 24)) + + mode: "", +>mode : Symbol(mode, Decl(comparableRelationBidirectional.ts, 28, 25)) + + }, + } as UserSettings, +>UserSettings : Symbol(UserSettings, Decl(comparableRelationBidirectional.ts, 13, 1)) + } +} + diff --git a/tests/baselines/reference/comparableRelationBidirectional.types b/tests/baselines/reference/comparableRelationBidirectional.types new file mode 100644 index 00000000000..9de6f03e265 --- /dev/null +++ b/tests/baselines/reference/comparableRelationBidirectional.types @@ -0,0 +1,72 @@ +=== tests/cases/compiler/comparableRelationBidirectional.ts === +enum AutomationMode { +>AutomationMode : AutomationMode + + NONE = "", +>NONE : AutomationMode.NONE +>"" : "" + + TIME = "time", +>TIME : AutomationMode.TIME +>"time" : "time" + + SYSTEM = "system", +>SYSTEM : AutomationMode.SYSTEM +>"system" : "system" + + LOCATION = "location", +>LOCATION : AutomationMode.LOCATION +>"location" : "location" +} + +interface ThemePreset { + id: string; +>id : string +} + +interface Automation { + mode: AutomationMode; +>mode : AutomationMode +} + +interface UserSettings { + presets: ThemePreset[]; +>presets : ThemePreset[] + + automation: Automation; +>automation : Automation +} + +interface ExtensionData { + settings: UserSettings; +>settings : UserSettings +} + +export function getMockData(): ExtensionData { +>getMockData : () => ExtensionData + + return { +>{ settings: { presets: [], automation: { mode: "", }, } as UserSettings, } : { settings: UserSettings; } + + settings: { +>settings : UserSettings +>{ presets: [], automation: { mode: "", }, } as UserSettings : UserSettings +>{ presets: [], automation: { mode: "", }, } : { presets: never[]; automation: { mode: ""; }; } + + presets: [], +>presets : never[] +>[] : never[] + + automation: { +>automation : { mode: ""; } +>{ mode: "", } : { mode: ""; } + + mode: "", +>mode : "" +>"" : "" + + }, + } as UserSettings, + } +} + diff --git a/tests/cases/compiler/comparableRelationBidirectional.ts b/tests/cases/compiler/comparableRelationBidirectional.ts new file mode 100644 index 00000000000..1b749473b7f --- /dev/null +++ b/tests/cases/compiler/comparableRelationBidirectional.ts @@ -0,0 +1,37 @@ +// @strict: true +// @noEmit: true + +enum AutomationMode { + NONE = "", + TIME = "time", + SYSTEM = "system", + LOCATION = "location", +} + +interface ThemePreset { + id: string; +} + +interface Automation { + mode: AutomationMode; +} + +interface UserSettings { + presets: ThemePreset[]; + automation: Automation; +} + +interface ExtensionData { + settings: UserSettings; +} + +export function getMockData(): ExtensionData { + return { + settings: { + presets: [], + automation: { + mode: "", + }, + } as UserSettings, + } +}