Skip to content

Commit

Permalink
Revert "Revert "Fixed an issue with contextual type for intersection …
Browse files Browse the repository at this point in the history
…properties (microsoft#48668)" (microsoft#50279)"

This reverts commit adf26ff.
  • Loading branch information
Andarist committed Jan 2, 2023
1 parent eb3d6fa commit f6074e8
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 3 deletions.
32 changes: 29 additions & 3 deletions src/compiler/checker.ts
Expand Up @@ -28416,15 +28416,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) {
return mapType(type, t => {
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;
const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
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);
Expand All @@ -28435,10 +28456,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
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
Expand Down
@@ -0,0 +1,42 @@
//// [contextualTypeFunctionObjectPropertyIntersection.ts]
type Action<TEvent extends { type: string }> = (ev: TEvent) => void;

interface MachineConfig<TEvent extends { type: string }> {
schema: {
events: TEvent;
};
on?: {
[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
} & {
"*"?: Action<TEvent>;
};
}

declare function createMachine<TEvent extends { type: string }>(
config: MachineConfig<TEvent>
): 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'
},
},
});
@@ -0,0 +1,82 @@
=== tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts ===
type Action<TEvent extends { type: string }> = (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<TEvent extends { type: string }> {
>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<TEvent extends { type: K } ? TEvent : never>;
>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<TEvent>;
>"*" : 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<TEvent extends { type: string }>(
>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<TEvent>
>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))

},
},
});

@@ -0,0 +1,73 @@
=== tests/cases/compiler/contextualTypeFunctionObjectPropertyIntersection.ts ===
type Action<TEvent extends { type: string }> = (ev: TEvent) => void;
>Action : Action<TEvent>
>type : string
>ev : TEvent

interface MachineConfig<TEvent extends { type: string }> {
>type : string

schema: {
>schema : { events: TEvent; }

events: TEvent;
>events : TEvent

};
on?: {
>on : ({ [K in TEvent["type"]]?: Action<TEvent extends { type: K; } ? TEvent : never> | undefined; } & { "*"?: Action<TEvent> | undefined; }) | undefined

[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
>type : K

} & {
"*"?: Action<TEvent>;
>"*" : Action<TEvent> | undefined

};
}

declare function createMachine<TEvent extends { type: string }>(
>createMachine : <TEvent extends { type: string; }>(config: MachineConfig<TEvent>) => void
>type : string

config: MachineConfig<TEvent>
>config : MachineConfig<TEvent>

): void;

createMachine({
>createMachine({ schema: { events: {} as { type: "FOO" } | { type: "BAR" }, }, on: { FOO: (ev) => { ev.type; // should be 'FOO' }, },}) : void
>createMachine : <TEvent extends { type: string; }>(config: MachineConfig<TEvent>) => 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"

},
},
});

@@ -0,0 +1,29 @@
// @strict: true

type Action<TEvent extends { type: string }> = (ev: TEvent) => void;

interface MachineConfig<TEvent extends { type: string }> {
schema: {
events: TEvent;
};
on?: {
[K in TEvent["type"]]?: Action<TEvent extends { type: K } ? TEvent : never>;
} & {
"*"?: Action<TEvent>;
};
}

declare function createMachine<TEvent extends { type: string }>(
config: MachineConfig<TEvent>
): void;

createMachine({
schema: {
events: {} as { type: "FOO" } | { type: "BAR" },
},
on: {
FOO: (ev) => {
ev.type; // should be 'FOO'
},
},
});

0 comments on commit f6074e8

Please sign in to comment.