Skip to content

Commit f6074e8

Browse files
committed
Revert "Revert "Fixed an issue with contextual type for intersection properties (microsoft#48668)" (microsoft#50279)"
This reverts commit adf26ff.
1 parent eb3d6fa commit f6074e8

5 files changed

+255
-3
lines changed

src/compiler/checker.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28416,15 +28416,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2841628416

2841728417
function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) {
2841828418
return mapType(type, t => {
28419+
if (t.flags & TypeFlags.Intersection) {
28420+
const intersection = t as IntersectionType;
28421+
let newTypes = mapDefined(intersection.types, getTypeOfConcretePropertyOfContextualType);
28422+
if (newTypes.length > 0) {
28423+
return getIntersectionType(newTypes);
28424+
}
28425+
newTypes = mapDefined(intersection.types, getTypeOfApplicableIndexInfoOfContextualType);
28426+
if (newTypes.length > 0) {
28427+
return getIntersectionType(newTypes);
28428+
}
28429+
return undefined;
28430+
}
28431+
const concretePropertyType = getTypeOfConcretePropertyOfContextualType(t);
28432+
if (concretePropertyType) {
28433+
return concretePropertyType;
28434+
}
28435+
return getTypeOfApplicableIndexInfoOfContextualType(t);
28436+
}, /*noReductions*/ true);
28437+
28438+
function getTypeOfConcretePropertyOfContextualType(t: Type) {
2841928439
if (isGenericMappedType(t) && !t.declaration.nameType) {
2842028440
const constraint = getConstraintTypeFromMappedType(t);
2842128441
const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint;
2842228442
const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
2842328443
if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) {
2842428444
return substituteIndexedMappedType(t, propertyNameType);
2842528445
}
28446+
return undefined;
2842628447
}
28427-
else if (t.flags & TypeFlags.StructuredType) {
28448+
if (t.flags & TypeFlags.StructuredType) {
2842828449
const prop = getPropertyOfType(t, name);
2842928450
if (prop) {
2843028451
return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop);
@@ -28435,10 +28456,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2843528456
return restType;
2843628457
}
2843728458
}
28438-
return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type;
2843928459
}
2844028460
return undefined;
28441-
}, /*noReductions*/ true);
28461+
}
28462+
function getTypeOfApplicableIndexInfoOfContextualType(t: Type) {
28463+
if (!(t.flags & TypeFlags.StructuredType)) {
28464+
return undefined;
28465+
}
28466+
return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type;
28467+
}
2844228468
}
2844328469

2844428470
// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [contextualTypeFunctionObjectPropertyIntersection.ts]
2+
type Action<TEvent extends { type: string }> = (ev: TEvent) => void;
3+
4+
interface MachineConfig<TEvent extends { type: string }> {
5+
schema: {
6+
events: TEvent;
7+
};
8+
on?: {
9+
[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
10+
} & {
11+
"*"?: Action<TEvent>;
12+
};
13+
}
14+
15+
declare function createMachine<TEvent extends { type: string }>(
16+
config: MachineConfig<TEvent>
17+
): void;
18+
19+
createMachine({
20+
schema: {
21+
events: {} as { type: "FOO" } | { type: "BAR" },
22+
},
23+
on: {
24+
FOO: (ev) => {
25+
ev.type; // should be 'FOO'
26+
},
27+
},
28+
});
29+
30+
31+
//// [contextualTypeFunctionObjectPropertyIntersection.js]
32+
"use strict";
33+
createMachine({
34+
schema: {
35+
events: {},
36+
},
37+
on: {
38+
FOO: function (ev) {
39+
ev.type; // should be 'FOO'
40+
},
41+
},
42+
});
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
=== tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts ===
2+
type Action<TEvent extends { type: string }> = (ev: TEvent) => void;
3+
>Action : Symbol(Action, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 0))
4+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 12))
5+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 28))
6+
>ev : Symbol(ev, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 48))
7+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 12))
8+
9+
interface MachineConfig<TEvent extends { type: string }> {
10+
>MachineConfig : Symbol(MachineConfig, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 68))
11+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24))
12+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 40))
13+
14+
schema: {
15+
>schema : Symbol(MachineConfig.schema, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 58))
16+
17+
events: TEvent;
18+
>events : Symbol(events, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 3, 11))
19+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24))
20+
21+
};
22+
on?: {
23+
>on : Symbol(MachineConfig.on, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 5, 4))
24+
25+
[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
26+
>K : Symbol(K, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 7, 5))
27+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24))
28+
>Action : Symbol(Action, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 0))
29+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24))
30+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 7, 51))
31+
>K : Symbol(K, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 7, 5))
32+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24))
33+
34+
} & {
35+
"*"?: Action<TEvent>;
36+
>"*" : Symbol("*", Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 8, 7))
37+
>Action : Symbol(Action, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 0))
38+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 2, 24))
39+
40+
};
41+
}
42+
43+
declare function createMachine<TEvent extends { type: string }>(
44+
>createMachine : Symbol(createMachine, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 11, 1))
45+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 31))
46+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 47))
47+
48+
config: MachineConfig<TEvent>
49+
>config : Symbol(config, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 64))
50+
>MachineConfig : Symbol(MachineConfig, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 0, 68))
51+
>TEvent : Symbol(TEvent, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 13, 31))
52+
53+
): void;
54+
55+
createMachine({
56+
>createMachine : Symbol(createMachine, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 11, 1))
57+
58+
schema: {
59+
>schema : Symbol(schema, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 17, 15))
60+
61+
events: {} as { type: "FOO" } | { type: "BAR" },
62+
>events : Symbol(events, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 18, 11))
63+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 19))
64+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 37))
65+
66+
},
67+
on: {
68+
>on : Symbol(on, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 20, 4))
69+
70+
FOO: (ev) => {
71+
>FOO : Symbol(FOO, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 21, 7))
72+
>ev : Symbol(ev, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 22, 10))
73+
74+
ev.type; // should be 'FOO'
75+
>ev.type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 19))
76+
>ev : Symbol(ev, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 22, 10))
77+
>type : Symbol(type, Decl(contextualTypeFunctionObjectPropertyIntersection.ts, 19, 19))
78+
79+
},
80+
},
81+
});
82+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
=== tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts ===
2+
type Action<TEvent extends { type: string }> = (ev: TEvent) => void;
3+
>Action : Action<TEvent>
4+
>type : string
5+
>ev : TEvent
6+
7+
interface MachineConfig<TEvent extends { type: string }> {
8+
>type : string
9+
10+
schema: {
11+
>schema : { events: TEvent; }
12+
13+
events: TEvent;
14+
>events : TEvent
15+
16+
};
17+
on?: {
18+
>on : ({ [K in TEvent["type"]]?: Action<TEvent extends { type: K; } ? TEvent : never> | undefined; } & { "*"?: Action<TEvent> | undefined; }) | undefined
19+
20+
[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
21+
>type : K
22+
23+
} & {
24+
"*"?: Action<TEvent>;
25+
>"*" : Action<TEvent> | undefined
26+
27+
};
28+
}
29+
30+
declare function createMachine<TEvent extends { type: string }>(
31+
>createMachine : <TEvent extends { type: string; }>(config: MachineConfig<TEvent>) => void
32+
>type : string
33+
34+
config: MachineConfig<TEvent>
35+
>config : MachineConfig<TEvent>
36+
37+
): void;
38+
39+
createMachine({
40+
>createMachine({ schema: { events: {} as { type: "FOO" } | { type: "BAR" }, }, on: { FOO: (ev) => { ev.type; // should be 'FOO' }, },}) : void
41+
>createMachine : <TEvent extends { type: string; }>(config: MachineConfig<TEvent>) => void
42+
>{ 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; }; }
43+
44+
schema: {
45+
>schema : { events: { type: "FOO"; } | { type: "BAR"; }; }
46+
>{ events: {} as { type: "FOO" } | { type: "BAR" }, } : { events: { type: "FOO"; } | { type: "BAR"; }; }
47+
48+
events: {} as { type: "FOO" } | { type: "BAR" },
49+
>events : { type: "FOO"; } | { type: "BAR"; }
50+
>{} as { type: "FOO" } | { type: "BAR" } : { type: "FOO"; } | { type: "BAR"; }
51+
>{} : {}
52+
>type : "FOO"
53+
>type : "BAR"
54+
55+
},
56+
on: {
57+
>on : { FOO: (ev: { type: "FOO"; }) => void; }
58+
>{ FOO: (ev) => { ev.type; // should be 'FOO' }, } : { FOO: (ev: { type: "FOO"; }) => void; }
59+
60+
FOO: (ev) => {
61+
>FOO : (ev: { type: "FOO"; }) => void
62+
>(ev) => { ev.type; // should be 'FOO' } : (ev: { type: "FOO"; }) => void
63+
>ev : { type: "FOO"; }
64+
65+
ev.type; // should be 'FOO'
66+
>ev.type : "FOO"
67+
>ev : { type: "FOO"; }
68+
>type : "FOO"
69+
70+
},
71+
},
72+
});
73+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @strict: true
2+
3+
type Action<TEvent extends { type: string }> = (ev: TEvent) => void;
4+
5+
interface MachineConfig<TEvent extends { type: string }> {
6+
schema: {
7+
events: TEvent;
8+
};
9+
on?: {
10+
[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
11+
} & {
12+
"*"?: Action<TEvent>;
13+
};
14+
}
15+
16+
declare function createMachine<TEvent extends { type: string }>(
17+
config: MachineConfig<TEvent>
18+
): void;
19+
20+
createMachine({
21+
schema: {
22+
events: {} as { type: "FOO" } | { type: "BAR" },
23+
},
24+
on: {
25+
FOO: (ev) => {
26+
ev.type; // should be 'FOO'
27+
},
28+
},
29+
});

0 commit comments

Comments
 (0)