From b6bb4119a51b4e195cb96d636e208db7537194a2 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 15 Aug 2022 16:44:56 -0700 Subject: [PATCH] Revert "Revert "Fixed an issue with contextual type for intersection properties (#48668)" (#50279)" This reverts commit adf26ffa4b00bda9b35b048b4f1083836451463e. --- src/compiler/checker.ts | 34 +++++++- ...lTypeFunctionObjectPropertyIntersection.js | 42 ++++++++++ ...FunctionObjectPropertyIntersection.symbols | 82 +++++++++++++++++++ ...peFunctionObjectPropertyIntersection.types | 73 +++++++++++++++++ ...lTypeFunctionObjectPropertyIntersection.ts | 29 +++++++ 5 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.js create mode 100644 tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.symbols create mode 100644 tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.types create mode 100644 tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dff83b717ec13..81489d7ae239f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27130,7 +27130,27 @@ namespace ts { } function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) { - return mapType(type, t => { + return mapType(type, (t): Type | undefined => { + if (t.flags & TypeFlags.Intersection) { + const intersection = t as IntersectionType; + let newTypes = mapDefined(intersection.types, getTypeOfConcretePropertyOfContextualType); + if (newTypes.length > 0) { + return getIntersectionType(newTypes); + } + newTypes = mapDefined(intersection.types, getTypeOfApplicableIndexInfoOfContextualType); + if (newTypes.length > 0) { + return getIntersectionType(newTypes); + } + return undefined; + } + const concretePropertyType = getTypeOfConcretePropertyOfContextualType(t); + if (concretePropertyType) { + return concretePropertyType; + } + return getTypeOfApplicableIndexInfoOfContextualType(t); + }, /*noReductions*/ true); + + function getTypeOfConcretePropertyOfContextualType(t: Type) { if (isGenericMappedType(t) && !t.declaration.nameType) { const constraint = getConstraintTypeFromMappedType(t); const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint; @@ -27138,8 +27158,9 @@ namespace ts { if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) { return substituteIndexedMappedType(t, propertyNameType); } + return undefined; } - else if (t.flags & TypeFlags.StructuredType) { + if (t.flags & TypeFlags.StructuredType) { const prop = getPropertyOfType(t, name); if (prop) { return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop); @@ -27150,10 +27171,15 @@ namespace ts { return restType; } } - return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type; } return undefined; - }, /*noReductions*/ true); + } + function getTypeOfApplicableIndexInfoOfContextualType(t: Type) { + if (!(t.flags & TypeFlags.StructuredType)) { + return undefined; + } + return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type; + } } // In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of diff --git a/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.js b/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.js new file mode 100644 index 0000000000000..65213deade435 --- /dev/null +++ b/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.js @@ -0,0 +1,42 @@ +//// [contextualTypeFunctionObjectPropertyIntersection.ts] +type Action = (ev: TEvent) => void; + +interface MachineConfig { + schema: { + events: TEvent; + }; + on?: { + [K in TEvent["type"]]?: Action; + } & { + "*"?: Action; + }; +} + +declare function createMachine( + config: MachineConfig +): void; + +createMachine({ + schema: { + events: {} as { type: "FOO" } | { type: "BAR" }, + }, + on: { + FOO: (ev) => { + ev.type; // should be 'FOO' + }, + }, +}); + + +//// [contextualTypeFunctionObjectPropertyIntersection.js] +"use strict"; +createMachine({ + schema: { + events: {} + }, + on: { + FOO: function (ev) { + ev.type; // should be 'FOO' + } + } +}); diff --git a/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.symbols b/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.symbols new file mode 100644 index 0000000000000..4690985350a17 --- /dev/null +++ b/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.symbols @@ -0,0 +1,82 @@ +=== tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts === +type Action = (ev: TEvent) => void; +>Action : Symbol(Action, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 0)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 12)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 28)) +>ev : Symbol(ev, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 48)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 12)) + +interface MachineConfig { +>MachineConfig : Symbol(MachineConfig, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 68)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 40)) + + schema: { +>schema : Symbol(MachineConfig.schema, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 58)) + + events: TEvent; +>events : Symbol(events, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 3, 11)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24)) + + }; + on?: { +>on : Symbol(MachineConfig.on, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 5, 4)) + + [K in TEvent["type"]]?: Action; +>K : Symbol(K, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 7, 5)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24)) +>Action : Symbol(Action, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 0)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 7, 51)) +>K : Symbol(K, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 7, 5)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24)) + + } & { + "*"?: Action; +>"*" : Symbol("*", Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 8, 7)) +>Action : Symbol(Action, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 0)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24)) + + }; +} + +declare function createMachine( +>createMachine : Symbol(createMachine, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 11, 1)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 31)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 47)) + + config: MachineConfig +>config : Symbol(config, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 64)) +>MachineConfig : Symbol(MachineConfig, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 68)) +>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 31)) + +): void; + +createMachine({ +>createMachine : Symbol(createMachine, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 11, 1)) + + schema: { +>schema : Symbol(schema, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 17, 15)) + + events: {} as { type: "FOO" } | { type: "BAR" }, +>events : Symbol(events, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 18, 11)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 19)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 37)) + + }, + on: { +>on : Symbol(on, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 20, 4)) + + FOO: (ev) => { +>FOO : Symbol(FOO, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 21, 7)) +>ev : Symbol(ev, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 22, 10)) + + ev.type; // should be 'FOO' +>ev.type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 19)) +>ev : Symbol(ev, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 22, 10)) +>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 19)) + + }, + }, +}); + diff --git a/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.types b/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.types new file mode 100644 index 0000000000000..30ba52626faa5 --- /dev/null +++ b/tests/baselines/reference/contextualTypeFunctionObjectPropertyIntersection.types @@ -0,0 +1,73 @@ +=== tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts === +type Action = (ev: TEvent) => void; +>Action : Action +>type : string +>ev : TEvent + +interface MachineConfig { +>type : string + + schema: { +>schema : { events: TEvent; } + + events: TEvent; +>events : TEvent + + }; + on?: { +>on : ({ [K in TEvent["type"]]?: Action | undefined; } & { "*"?: Action | undefined; }) | undefined + + [K in TEvent["type"]]?: Action; +>type : K + + } & { + "*"?: Action; +>"*" : Action | undefined + + }; +} + +declare function createMachine( +>createMachine : (config: MachineConfig) => void +>type : string + + config: MachineConfig +>config : MachineConfig + +): void; + +createMachine({ +>createMachine({ schema: { events: {} as { type: "FOO" } | { type: "BAR" }, }, on: { FOO: (ev) => { ev.type; // should be 'FOO' }, },}) : void +>createMachine : (config: MachineConfig) => void +>{ schema: { events: {} as { type: "FOO" } | { type: "BAR" }, }, on: { FOO: (ev) => { ev.type; // should be 'FOO' }, },} : { schema: { events: { type: "FOO"; } | { type: "BAR"; }; }; on: { FOO: (ev: { type: "FOO"; }) => void; }; } + + schema: { +>schema : { events: { type: "FOO"; } | { type: "BAR"; }; } +>{ events: {} as { type: "FOO" } | { type: "BAR" }, } : { events: { type: "FOO"; } | { type: "BAR"; }; } + + events: {} as { type: "FOO" } | { type: "BAR" }, +>events : { type: "FOO"; } | { type: "BAR"; } +>{} as { type: "FOO" } | { type: "BAR" } : { type: "FOO"; } | { type: "BAR"; } +>{} : {} +>type : "FOO" +>type : "BAR" + + }, + on: { +>on : { FOO: (ev: { type: "FOO"; }) => void; } +>{ FOO: (ev) => { ev.type; // should be 'FOO' }, } : { FOO: (ev: { type: "FOO"; }) => void; } + + FOO: (ev) => { +>FOO : (ev: { type: "FOO"; }) => void +>(ev) => { ev.type; // should be 'FOO' } : (ev: { type: "FOO"; }) => void +>ev : { type: "FOO"; } + + ev.type; // should be 'FOO' +>ev.type : "FOO" +>ev : { type: "FOO"; } +>type : "FOO" + + }, + }, +}); + diff --git a/tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts b/tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts new file mode 100644 index 0000000000000..2d7e00f32f65e --- /dev/null +++ b/tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts @@ -0,0 +1,29 @@ +// @strict: true + +type Action = (ev: TEvent) => void; + +interface MachineConfig { + schema: { + events: TEvent; + }; + on?: { + [K in TEvent["type"]]?: Action; + } & { + "*"?: Action; + }; +} + +declare function createMachine( + config: MachineConfig +): void; + +createMachine({ + schema: { + events: {} as { type: "FOO" } | { type: "BAR" }, + }, + on: { + FOO: (ev) => { + ev.type; // should be 'FOO' + }, + }, +});