diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6233873ab6de1..4ec45c9c55389 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31578,30 +31578,40 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) { - return mapType(type, t => { - if (isGenericMappedType(t) && !t.declaration.nameType) { - const constraint = getConstraintTypeFromMappedType(t); + return mapType(type, getTypeOfProperty, /*noReductions*/ true); + function getTypeOfProperty(type: Type): Type | undefined { + if (type.flags & TypeFlags.Intersection) { + // Intersections that include `any` resolve to `any`. This loses potentially useful type + // information contributed by other types in the intersection, so we map `any` to `unknown` + // when we intersect the contextual property types. + return getIntersectionType(map((type as IntersectionType).types, t => { + const propType = getTypeOfProperty(t); + return !propType || propType.flags & TypeFlags.Any ? unknownType : propType; + })); + } + else if (isGenericMappedType(type) && !type.declaration.nameType) { + const constraint = getConstraintTypeFromMappedType(type); const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint; const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name)); if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) { - return substituteIndexedMappedType(t, propertyNameType); + return substituteIndexedMappedType(type, propertyNameType); } } - else if (t.flags & TypeFlags.StructuredType) { - const prop = getPropertyOfType(t, name); + else if (type.flags & TypeFlags.Object) { + const prop = getPropertyOfType(type, name); if (prop) { return isCircularMappedProperty(prop) ? undefined : removeMissingType(getTypeOfSymbol(prop), !!(prop.flags & SymbolFlags.Optional)); } - if (isTupleType(t) && isNumericLiteralName(name) && +name >= 0) { - const restType = getElementTypeOfSliceOfTupleType(t, t.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true); + if (isTupleType(type) && isNumericLiteralName(name) && +name >= 0) { + const restType = getElementTypeOfSliceOfTupleType(type, type.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true); if (restType) { return restType; } } - return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type; + return findApplicableIndexInfo(getIndexInfosOfStructuredType(type), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type; } return undefined; - }, /*noReductions*/ true); + } } // 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/contextualIntersectionTypes.symbols b/tests/baselines/reference/contextualIntersectionTypes.symbols new file mode 100644 index 0000000000000..2eb6fa1406278 --- /dev/null +++ b/tests/baselines/reference/contextualIntersectionTypes.symbols @@ -0,0 +1,129 @@ +//// [tests/cases/compiler/contextualIntersectionTypes.ts] //// + +=== contextualIntersectionTypes.ts === +// https://github.com/microsoft/TypeScript/issues/59473 + +const a: [any] & [1] = [1]; +>a : Symbol(a, Decl(contextualIntersectionTypes.ts, 2, 5)) + +const b: { ml: any } & { ml: 'edge' } = { ml: 'edge' }; +>b : Symbol(b, Decl(contextualIntersectionTypes.ts, 3, 5)) +>ml : Symbol(ml, Decl(contextualIntersectionTypes.ts, 3, 10)) +>ml : Symbol(ml, Decl(contextualIntersectionTypes.ts, 3, 24)) +>ml : Symbol(ml, Decl(contextualIntersectionTypes.ts, 3, 41)) + +// https://github.com/microsoft/TypeScript/issues/48812 + +type Action = (ev: TEvent) => void; +>Action : Symbol(Action, Decl(contextualIntersectionTypes.ts, 3, 55)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 7, 12)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 7, 28)) +>ev : Symbol(ev, Decl(contextualIntersectionTypes.ts, 7, 48)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 7, 12)) + +interface MachineConfig { +>MachineConfig : Symbol(MachineConfig, Decl(contextualIntersectionTypes.ts, 7, 68)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 9, 24)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 9, 40)) + + schema: { +>schema : Symbol(MachineConfig.schema, Decl(contextualIntersectionTypes.ts, 9, 58)) + + events: TEvent; +>events : Symbol(events, Decl(contextualIntersectionTypes.ts, 10, 11)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 9, 24)) + + }; + on?: { +>on : Symbol(MachineConfig.on, Decl(contextualIntersectionTypes.ts, 12, 4)) + + [K in TEvent["type"]]?: Action; +>K : Symbol(K, Decl(contextualIntersectionTypes.ts, 14, 5)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 9, 24)) +>Action : Symbol(Action, Decl(contextualIntersectionTypes.ts, 3, 55)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 9, 24)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 14, 51)) +>K : Symbol(K, Decl(contextualIntersectionTypes.ts, 14, 5)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 9, 24)) + + } & { + "*"?: Action; +>"*" : Symbol("*", Decl(contextualIntersectionTypes.ts, 15, 7)) +>Action : Symbol(Action, Decl(contextualIntersectionTypes.ts, 3, 55)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 9, 24)) + + }; +} + +declare function createMachine( +>createMachine : Symbol(createMachine, Decl(contextualIntersectionTypes.ts, 18, 1)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 20, 31)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 20, 47)) + + config: MachineConfig +>config : Symbol(config, Decl(contextualIntersectionTypes.ts, 20, 64)) +>MachineConfig : Symbol(MachineConfig, Decl(contextualIntersectionTypes.ts, 7, 68)) +>TEvent : Symbol(TEvent, Decl(contextualIntersectionTypes.ts, 20, 31)) + +): void; + +createMachine({ +>createMachine : Symbol(createMachine, Decl(contextualIntersectionTypes.ts, 18, 1)) + + schema: { +>schema : Symbol(schema, Decl(contextualIntersectionTypes.ts, 24, 15)) + + events: {} as { type: "FOO" } | { type: "BAR" }, +>events : Symbol(events, Decl(contextualIntersectionTypes.ts, 25, 11)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 26, 19)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 26, 37)) + + }, + on: { +>on : Symbol(on, Decl(contextualIntersectionTypes.ts, 27, 4)) + + FOO: (ev) => { +>FOO : Symbol(FOO, Decl(contextualIntersectionTypes.ts, 28, 7)) +>ev : Symbol(ev, Decl(contextualIntersectionTypes.ts, 29, 10)) + + ev.type; +>ev.type : Symbol(type, Decl(contextualIntersectionTypes.ts, 26, 19)) +>ev : Symbol(ev, Decl(contextualIntersectionTypes.ts, 29, 10)) +>type : Symbol(type, Decl(contextualIntersectionTypes.ts, 26, 19)) + + }, + }, +}); + +// https://github.com/microsoft/TypeScript/issues/49307#issuecomment-1196014488 + +type Validate = T & { [K in keyof T]: object } +>Validate : Symbol(Validate, Decl(contextualIntersectionTypes.ts, 33, 3)) +>T : Symbol(T, Decl(contextualIntersectionTypes.ts, 37, 14)) +>T : Symbol(T, Decl(contextualIntersectionTypes.ts, 37, 14)) +>K : Symbol(K, Decl(contextualIntersectionTypes.ts, 37, 26)) +>T : Symbol(T, Decl(contextualIntersectionTypes.ts, 37, 14)) + +declare function f any>>(s: S, x: Validate): void; +>f : Symbol(f, Decl(contextualIntersectionTypes.ts, 37, 49)) +>S : Symbol(S, Decl(contextualIntersectionTypes.ts, 38, 19)) +>T : Symbol(T, Decl(contextualIntersectionTypes.ts, 38, 21)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>state : Symbol(state, Decl(contextualIntersectionTypes.ts, 38, 48)) +>S : Symbol(S, Decl(contextualIntersectionTypes.ts, 38, 19)) +>s : Symbol(s, Decl(contextualIntersectionTypes.ts, 38, 67)) +>S : Symbol(S, Decl(contextualIntersectionTypes.ts, 38, 19)) +>x : Symbol(x, Decl(contextualIntersectionTypes.ts, 38, 72)) +>Validate : Symbol(Validate, Decl(contextualIntersectionTypes.ts, 33, 3)) +>T : Symbol(T, Decl(contextualIntersectionTypes.ts, 38, 21)) + +f(0, { +>f : Symbol(f, Decl(contextualIntersectionTypes.ts, 37, 49)) + + foo: s => s + 1, +>foo : Symbol(foo, Decl(contextualIntersectionTypes.ts, 40, 6)) +>s : Symbol(s, Decl(contextualIntersectionTypes.ts, 41, 6)) +>s : Symbol(s, Decl(contextualIntersectionTypes.ts, 41, 6)) + +}) + diff --git a/tests/baselines/reference/contextualIntersectionTypes.types b/tests/baselines/reference/contextualIntersectionTypes.types new file mode 100644 index 0000000000000..a10efc4258b47 --- /dev/null +++ b/tests/baselines/reference/contextualIntersectionTypes.types @@ -0,0 +1,172 @@ +//// [tests/cases/compiler/contextualIntersectionTypes.ts] //// + +=== contextualIntersectionTypes.ts === +// https://github.com/microsoft/TypeScript/issues/59473 + +const a: [any] & [1] = [1]; +>a : [any] & [1] +> : ^^^^^^^^^^^ +>[1] : [1] +> : ^^^ +>1 : 1 +> : ^ + +const b: { ml: any } & { ml: 'edge' } = { ml: 'edge' }; +>b : { ml: any; } & { ml: "edge"; } +> : ^^^^^^ ^^^^^^^^^^^^ ^^^ +>ml : any +>ml : "edge" +> : ^^^^^^ +>{ ml: 'edge' } : { ml: "edge"; } +> : ^^^^^^^^^^^^^^^ +>ml : "edge" +> : ^^^^^^ +>'edge' : "edge" +> : ^^^^^^ + +// https://github.com/microsoft/TypeScript/issues/48812 + +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 +> : ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ + + [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; }, },}) : void +> : ^^^^ +>createMachine : (config: MachineConfig) => void +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ schema: { events: {} as { type: "FOO" } | { type: "BAR" }, }, on: { FOO: (ev) => { ev.type; }, },} : { 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; }, } : { FOO: (ev: { type: "FOO"; }) => void; } +> : ^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^ + + FOO: (ev) => { +>FOO : (ev: { type: "FOO"; }) => void +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^ +>(ev) => { ev.type; } : (ev: { type: "FOO"; }) => void +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^ +>ev : { type: "FOO"; } +> : ^^^^^^^^ ^^^ + + ev.type; +>ev.type : "FOO" +> : ^^^^^ +>ev : { type: "FOO"; } +> : ^^^^^^^^ ^^^ +>type : "FOO" +> : ^^^^^ + + }, + }, +}); + +// https://github.com/microsoft/TypeScript/issues/49307#issuecomment-1196014488 + +type Validate = T & { [K in keyof T]: object } +>Validate : Validate +> : ^^^^^^^^^^^ + +declare function f any>>(s: S, x: Validate): void; +>f : any>>(s: S, x: Validate) => void +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>state : S +> : ^ +>s : S +> : ^ +>x : Validate +> : ^^^^^^^^^^^ + +f(0, { +>f(0, { foo: s => s + 1,}) : void +> : ^^^^ +>f : any>>(s: S, x: Validate) => void +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^ +>0 : 0 +> : ^ +>{ foo: s => s + 1,} : { foo: (s: number) => number; } +> : ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ + + foo: s => s + 1, +>foo : (s: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>s => s + 1 : (s: number) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>s : number +> : ^^^^^^ +>s + 1 : number +> : ^^^^^^ +>s : number +> : ^^^^^^ +>1 : 1 +> : ^ + +}) + diff --git a/tests/cases/compiler/contextualIntersectionTypes.ts b/tests/cases/compiler/contextualIntersectionTypes.ts new file mode 100644 index 0000000000000..03050ab9eeb2f --- /dev/null +++ b/tests/cases/compiler/contextualIntersectionTypes.ts @@ -0,0 +1,46 @@ +// @strict: true +// @noemit: true + +// https://github.com/microsoft/TypeScript/issues/59473 + +const a: [any] & [1] = [1]; +const b: { ml: any } & { ml: 'edge' } = { ml: 'edge' }; + +// https://github.com/microsoft/TypeScript/issues/48812 + +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; + }, + }, +}); + +// https://github.com/microsoft/TypeScript/issues/49307#issuecomment-1196014488 + +type Validate = T & { [K in keyof T]: object } +declare function f any>>(s: S, x: Validate): void; + +f(0, { + foo: s => s + 1, +})