diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index 4bb9d2923d9ab..ab30343d2f734 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -1053,14 +1053,10 @@ func (c *Checker) createReverseMappedType(source *Type, target *Type, constraint return reversed } -// We consider a type to be partially inferable if it isn't marked non-inferable or if it is -// an object literal type with at least one property of an inferable type. For example, an object -// literal { a: 123, b: x => true } is marked non-inferable because it contains a context sensitive -// arrow function, but is considered partially inferable because property 'a' has an inferable type. +// We consider a type to be partially inferable if it isn't marked non-inferable. Even when every property is context sensitive, +// reverse mapped types can still infer the object's keys and defer inference from its property values. func (c *Checker) isPartiallyInferableType(t *Type) bool { - return t.objectFlags&ObjectFlagsNonInferrableType == 0 || isObjectLiteralType(t) && core.Some(c.getPropertiesOfType(t), func(prop *ast.Symbol) bool { - return c.isPartiallyInferableType(c.getTypeOfSymbol(prop)) - }) || isTupleType(t) && core.Some(c.getElementTypes(t), c.isPartiallyInferableType) + return t.objectFlags&ObjectFlagsNonInferrableType == 0 || isObjectLiteralType(t) || isTupleType(t) } func (c *Checker) inferReverseMappedType(source *Type, target *Type, constraint *Type) *Type { diff --git a/tsc/testdata/baselines/reference/compiler/reverseMappedPartiallyInferableTypes.types b/tsc/testdata/baselines/reference/compiler/reverseMappedPartiallyInferableTypes.types index c25702df0bbf5..4f80a4747c819 100644 --- a/tsc/testdata/baselines/reference/compiler/reverseMappedPartiallyInferableTypes.types +++ b/tsc/testdata/baselines/reference/compiler/reverseMappedPartiallyInferableTypes.types @@ -208,8 +208,8 @@ const obj2 = id({ // No properties have inferable types const obj3 = id({ ->obj3 : Mapped ->id({ foo: { contains(k) { return k.length > 0; } }}) : Mapped +>obj3 : Mapped<{ foo: unknown; }> +>id({ foo: { contains(k) { return k.length > 0; } }}) : Mapped<{ foo: unknown; }> >id : (arg: Mapped) => Mapped >{ foo: { contains(k) { return k.length > 0; } }} : { foo: { contains(k: unknown): boolean; }; } diff --git a/tsc/testdata/baselines/reference/compiler/reverseMappedTypeWithAllContextSensitiveProperties.symbols b/tsc/testdata/baselines/reference/compiler/reverseMappedTypeWithAllContextSensitiveProperties.symbols new file mode 100644 index 0000000000000..4f35da315ed43 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/reverseMappedTypeWithAllContextSensitiveProperties.symbols @@ -0,0 +1,112 @@ +//// [tests/cases/compiler/reverseMappedTypeWithAllContextSensitiveProperties.ts] //// + +=== reverseMappedTypeWithAllContextSensitiveProperties.ts === +createMachine({ +>createMachine : Symbol(createMachine, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 13, 13)) + + initial: "a", +>initial : Symbol(initial, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 0, 15)) + + context: { foo: 1 }, +>context : Symbol(context, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 1, 15)) +>foo : Symbol(foo, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 2, 12)) + + states: { +>states : Symbol(states, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 2, 22)) + + a: { +>a : Symbol(a, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 3, 11)) + + entry: (context, state) => { +>entry : Symbol(entry, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 4, 8)) +>context : Symbol(context, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 5, 14)) +>state : Symbol(state, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 5, 22)) + + context.foo.toFixed(); +>context.foo.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>context.foo : Symbol(foo, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 2, 12)) +>context : Symbol(context, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 5, 14)) +>foo : Symbol(foo, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 2, 12)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + + const currentState: "a" = state; +>currentState : Symbol(currentState, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 7, 13)) +>state : Symbol(state, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 5, 22)) + } + } + } +}) + +declare const createMachine: +>createMachine : Symbol(createMachine, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 13, 13)) + + D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) + + initial: keyof D["states"], +>initial : Symbol(initial, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 14)) +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) + + context: D["context"] extends object ? D["context"] : object, +>context : Symbol(context, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 15, 31)) +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) + + states: { +>states : Symbol(states, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 16, 65)) + + [S in keyof D["states"]]: { +>S : Symbol(S, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 18, 7)) +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) + + entry?: (context: D["context"], state: S) => void +>entry : Symbol(entry, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 18, 33)) +>context : Symbol(context, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 19, 17)) +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) +>state : Symbol(state, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 19, 39)) +>S : Symbol(S, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 18, 7)) + } + } + }> + (definition: IdentityObject) => +>definition : Symbol(definition, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 23, 5)) +>IdentityObject : Symbol(IdentityObject, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 32, 11)) +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) + + D +>D : Symbol(D, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 14, 3)) + +type Identity = +>Identity : Symbol(Identity, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 24, 7)) +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) + + T extends any +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) + + ? ( T extends (...a: never) => unknown ? T : +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) +>a : Symbol(a, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 28, 19)) +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) + + T extends object ? IdentityObject : +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) +>IdentityObject : Symbol(IdentityObject, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 32, 11)) +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) + + T +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 26, 14)) + + ) + : never + +type IdentityObject = +>IdentityObject : Symbol(IdentityObject, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 32, 11)) +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 34, 20)) + + { [K in keyof T]: Identity } +>K : Symbol(K, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 35, 5)) +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 34, 20)) +>Identity : Symbol(Identity, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 24, 7)) +>T : Symbol(T, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 34, 20)) +>K : Symbol(K, Decl(reverseMappedTypeWithAllContextSensitiveProperties.ts, 35, 5)) + diff --git a/tsc/testdata/baselines/reference/compiler/reverseMappedTypeWithAllContextSensitiveProperties.types b/tsc/testdata/baselines/reference/compiler/reverseMappedTypeWithAllContextSensitiveProperties.types new file mode 100644 index 0000000000000..b37dfb6640a45 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/reverseMappedTypeWithAllContextSensitiveProperties.types @@ -0,0 +1,91 @@ +//// [tests/cases/compiler/reverseMappedTypeWithAllContextSensitiveProperties.ts] //// + +=== reverseMappedTypeWithAllContextSensitiveProperties.ts === +createMachine({ +>createMachine({ initial: "a", context: { foo: 1 }, states: { a: { entry: (context, state) => { context.foo.toFixed(); const currentState: "a" = state; } } }}) : { initial: "a"; context: { foo: number; }; states: { a: { entry?: ((context: { foo: number; }, state: "a") => void) | undefined; }; }; } +>createMachine : void; }; }; }>(definition: IdentityObject) => D +>{ initial: "a", context: { foo: 1 }, states: { a: { entry: (context, state) => { context.foo.toFixed(); const currentState: "a" = state; } } }} : { initial: "a"; context: { foo: number; }; states: { a: { entry: (context: { foo: number; }, state: "a") => void; }; }; } + + initial: "a", +>initial : "a" +>"a" : "a" + + context: { foo: 1 }, +>context : { foo: number; } +>{ foo: 1 } : { foo: number; } +>foo : number +>1 : 1 + + states: { +>states : { a: { entry: (context: { foo: number; }, state: "a") => void; }; } +>{ a: { entry: (context, state) => { context.foo.toFixed(); const currentState: "a" = state; } } } : { a: { entry: (context: { foo: number; }, state: "a") => void; }; } + + a: { +>a : { entry: (context: { foo: number; }, state: "a") => void; } +>{ entry: (context, state) => { context.foo.toFixed(); const currentState: "a" = state; } } : { entry: (context: { foo: number; }, state: "a") => void; } + + entry: (context, state) => { +>entry : (context: { foo: number; }, state: "a") => void +>(context, state) => { context.foo.toFixed(); const currentState: "a" = state; } : (context: { foo: number; }, state: "a") => void +>context : { foo: number; } +>state : "a" + + context.foo.toFixed(); +>context.foo.toFixed() : string +>context.foo.toFixed : (fractionDigits?: number) => string +>context.foo : number +>context : { foo: number; } +>foo : number +>toFixed : (fractionDigits?: number) => string + + const currentState: "a" = state; +>currentState : "a" +>state : "a" + } + } + } +}) + +declare const createMachine: +>createMachine : void; }; }; }>(definition: IdentityObject) => D + + initial : keyof D["states"] + + context: D["context"] extends object ? D["context"] : object, +>context : D["context"] extends object ? D["context"] : object + + states: { +>states : { [S in keyof D["states"]]: { entry?: (context: D["context"], state: S) => void; }; } + + [S in keyof D["states"]]: { + entry?: (context: D["context"], state: S) => void +>entry : ((context: D["context"], state: S) => void) | undefined +>context : D["context"] +>state : S + } + } + }> + (definition: IdentityObject) => +>definition : IdentityObject + + D + +type Identity = +>Identity : Identity + + T extends any + ? ( T extends (...a: never) => unknown ? T : +>a : never + + T extends object ? IdentityObject : + T + ) + : never + +type IdentityObject = +>IdentityObject : IdentityObject + + { [K in keyof T]: Identity } + diff --git a/tsc/testdata/tests/cases/compiler/reverseMappedTypeWithAllContextSensitiveProperties.ts b/tsc/testdata/tests/cases/compiler/reverseMappedTypeWithAllContextSensitiveProperties.ts new file mode 100644 index 0000000000000..64b80015c493d --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/reverseMappedTypeWithAllContextSensitiveProperties.ts @@ -0,0 +1,39 @@ +// @strict: true +// @noEmit: true + +createMachine({ + initial: "a", + context: { foo: 1 }, + states: { + a: { + entry: (context, state) => { + context.foo.toFixed(); + const currentState: "a" = state; + } + } + } +}) + +declare const createMachine: + void + } + } + }> + (definition: IdentityObject) => + D + +type Identity = + T extends any + ? ( T extends (...a: never) => unknown ? T : + T extends object ? IdentityObject : + T + ) + : never + +type IdentityObject = + { [K in keyof T]: Identity }