diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9e1d960030920..90e3a3d2e24de 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -403,10 +403,10 @@ namespace ts { const wildcardType = createIntrinsicType(TypeFlags.Any, "any"); const errorType = createIntrinsicType(TypeFlags.Any, "error"); const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); - const undefinedType = createNullableType(TypeFlags.Undefined, "undefined", 0); - const undefinedWideningType = strictNullChecks ? undefinedType : createNullableType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType); - const nullType = createNullableType(TypeFlags.Null, "null", 0); - const nullWideningType = strictNullChecks ? nullType : createNullableType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType); + const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); + const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType); + const nullType = createIntrinsicType(TypeFlags.Null, "null"); + const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType); const stringType = createIntrinsicType(TypeFlags.String, "string"); const numberType = createIntrinsicType(TypeFlags.Number, "number"); const bigintType = createIntrinsicType(TypeFlags.BigInt, "bigint"); @@ -432,6 +432,7 @@ namespace ts { const voidType = createIntrinsicType(TypeFlags.Void, "void"); const neverType = createIntrinsicType(TypeFlags.Never, "never"); const silentNeverType = createIntrinsicType(TypeFlags.Never, "never"); + const nonInferrableType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType); const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never"); const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object"); const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]); @@ -452,7 +453,7 @@ namespace ts { const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated // in getPropagatingFlagsOfTypes, and it is checked in inferFromTypes. - anyFunctionType.objectFlags |= ObjectFlags.ContainsAnyFunctionType; + anyFunctionType.objectFlags |= ObjectFlags.NonInferrableType; const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); @@ -2753,14 +2754,9 @@ namespace ts { return result; } - function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType { + function createIntrinsicType(kind: TypeFlags, intrinsicName: string, objectFlags: ObjectFlags = 0): IntrinsicType { const type = createType(kind); type.intrinsicName = intrinsicName; - return type; - } - - function createNullableType(kind: TypeFlags, intrinsicName: string, objectFlags: ObjectFlags): NullableType { - const type = createIntrinsicType(kind, intrinsicName); type.objectFlags = objectFlags; return type; } @@ -14137,7 +14133,7 @@ namespace ts { const result = createAnonymousType(type.symbol, members, emptyArray, emptyArray, stringIndexInfo && createIndexInfo(getWidenedType(stringIndexInfo.type), stringIndexInfo.isReadonly), numberIndexInfo && createIndexInfo(getWidenedType(numberIndexInfo.type), numberIndexInfo.isReadonly)); - result.objectFlags |= (getObjectFlags(type) & ObjectFlags.JSLiteral); // Retain js literal flag through widening + result.objectFlags |= (getObjectFlags(type) & (ObjectFlags.JSLiteral | ObjectFlags.NonInferrableType)); // Retain js literal flag through widening return result; } @@ -14455,16 +14451,10 @@ namespace ts { } function createReverseMappedType(source: Type, target: MappedType, constraint: IndexType) { - const properties = getPropertiesOfType(source); - if (properties.length === 0 && !getIndexInfoOfType(source, IndexKind.String)) { - return undefined; - } // If any property contains context sensitive functions that have been skipped, the source type // is incomplete and we can't infer a meaningful input type. - for (const prop of properties) { - if (getObjectFlags(getTypeOfSymbol(prop)) & ObjectFlags.ContainsAnyFunctionType) { - return undefined; - } + if (getObjectFlags(source) & ObjectFlags.NonInferrableType || getPropertiesOfType(source).length === 0 && !getIndexInfoOfType(source, IndexKind.String)) { + return undefined; } // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been // applied to the element type(s). @@ -14619,7 +14609,7 @@ namespace ts { // not contain anyFunctionType when we come back to this argument for its second round // of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard // when constructing types from type parameters that had no inference candidates). - if (getObjectFlags(source) & ObjectFlags.ContainsAnyFunctionType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) { + if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType))) { return; } const inference = getInferenceInfoForType(target); @@ -14932,7 +14922,7 @@ namespace ts { const sourceLen = sourceSignatures.length; const targetLen = targetSignatures.length; const len = sourceLen < targetLen ? sourceLen : targetLen; - const skipParameters = !!(getObjectFlags(source) & ObjectFlags.ContainsAnyFunctionType); + const skipParameters = !!(getObjectFlags(source) & ObjectFlags.NonInferrableType); for (let i = 0; i < len; i++) { inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]), skipParameters); } @@ -21052,7 +21042,7 @@ namespace ts { // returns a function type, we choose to defer processing. This narrowly permits function composition // operators to flow inferences through return types, but otherwise processes calls right away. We // use the resolvingSignature singleton to indicate that we deferred processing. This result will be - // propagated out and eventually turned into silentNeverType (a type that is assignable to anything and + // propagated out and eventually turned into nonInferrableType (a type that is assignable to anything and // from which we never make inferences). if (checkMode & CheckMode.SkipGenericFunctions && !node.typeArguments && callSignatures.some(isGenericFunctionReturningFunction)) { skippedGenericFunction(node, checkMode); @@ -21535,8 +21525,8 @@ namespace ts { const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode); if (signature === resolvingSignature) { // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that - // returns a function type. We defer checking and return anyFunctionType. - return silentNeverType; + // returns a function type. We defer checking and return nonInferrableType. + return nonInferrableType; } if (node.expression.kind === SyntaxKind.SuperKeyword) { @@ -22343,7 +22333,7 @@ namespace ts { const returnType = getReturnTypeFromBody(node, checkMode); const returnOnlySignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, undefined, undefined); - returnOnlyType.objectFlags |= ObjectFlags.ContainsAnyFunctionType; + returnOnlyType.objectFlags |= ObjectFlags.NonInferrableType; return links.contextFreeType = returnOnlyType; } return anyFunctionType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bcc3218a017e7..e2abe7ddd953f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3944,7 +3944,7 @@ namespace ts { Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, StructuredOrInstantiable = StructuredType | Instantiable, /* @internal */ - ObjectFlagsType = Nullable | Object | Union | Intersection, + ObjectFlagsType = Nullable | Never | Object | Union | Intersection, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive, @@ -4064,12 +4064,12 @@ namespace ts { /* @internal */ ContainsObjectLiteral = 1 << 18, // Type is or contains object literal type /* @internal */ - ContainsAnyFunctionType = 1 << 19, // Type is or contains the anyFunctionType + NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType ClassOrInterface = Class | Interface, /* @internal */ RequiresWidening = ContainsWideningType | ContainsObjectLiteral, /* @internal */ - PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType + PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | NonInferrableType } /* @internal */ diff --git a/tests/baselines/reference/genericFunctionInference2.js b/tests/baselines/reference/genericFunctionInference2.js new file mode 100644 index 0000000000000..e8967ed8b8a57 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference2.js @@ -0,0 +1,46 @@ +//// [genericFunctionInference2.ts] +// Repro from #30685 + +type Reducer = (state: S) => S; +declare function combineReducers(reducers: { [K in keyof S]: Reducer }): Reducer; + +type MyState = { combined: { foo: number } }; +declare const foo: Reducer; + +const myReducer1: Reducer = combineReducers({ + combined: combineReducers({ foo }), +}); + +const myReducer2 = combineReducers({ + combined: combineReducers({ foo }), +}); + +// Repro from #30942 + +declare function withH(handlerCreators: HandleCreatorsFactory): U; + +type Props = { out: number } + +type HandleCreatorsFactory = (initialProps: T) => { [P in keyof U]: (props: T) => U[P] }; + +const enhancer4 = withH((props: Props) => ({ + onChange: (props) => (e: any) => {}, + onSubmit: (props) => (e: any) => {}, +})); + +enhancer4.onChange(null); + + +//// [genericFunctionInference2.js] +// Repro from #30685 +var myReducer1 = combineReducers({ + combined: combineReducers({ foo: foo }) +}); +var myReducer2 = combineReducers({ + combined: combineReducers({ foo: foo }) +}); +var enhancer4 = withH(function (props) { return ({ + onChange: function (props) { return function (e) { }; }, + onSubmit: function (props) { return function (e) { }; } +}); }); +enhancer4.onChange(null); diff --git a/tests/baselines/reference/genericFunctionInference2.symbols b/tests/baselines/reference/genericFunctionInference2.symbols new file mode 100644 index 0000000000000..ca099b062eb43 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference2.symbols @@ -0,0 +1,108 @@ +=== tests/cases/compiler/genericFunctionInference2.ts === +// Repro from #30685 + +type Reducer = (state: S) => S; +>Reducer : Symbol(Reducer, Decl(genericFunctionInference2.ts, 0, 0)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 2, 13)) +>state : Symbol(state, Decl(genericFunctionInference2.ts, 2, 19)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 2, 13)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 2, 13)) + +declare function combineReducers(reducers: { [K in keyof S]: Reducer }): Reducer; +>combineReducers : Symbol(combineReducers, Decl(genericFunctionInference2.ts, 2, 34)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 3, 33)) +>reducers : Symbol(reducers, Decl(genericFunctionInference2.ts, 3, 36)) +>K : Symbol(K, Decl(genericFunctionInference2.ts, 3, 49)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 3, 33)) +>Reducer : Symbol(Reducer, Decl(genericFunctionInference2.ts, 0, 0)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 3, 33)) +>K : Symbol(K, Decl(genericFunctionInference2.ts, 3, 49)) +>Reducer : Symbol(Reducer, Decl(genericFunctionInference2.ts, 0, 0)) +>S : Symbol(S, Decl(genericFunctionInference2.ts, 3, 33)) + +type MyState = { combined: { foo: number } }; +>MyState : Symbol(MyState, Decl(genericFunctionInference2.ts, 3, 93)) +>combined : Symbol(combined, Decl(genericFunctionInference2.ts, 5, 16)) +>foo : Symbol(foo, Decl(genericFunctionInference2.ts, 5, 28)) + +declare const foo: Reducer; +>foo : Symbol(foo, Decl(genericFunctionInference2.ts, 6, 13)) +>Reducer : Symbol(Reducer, Decl(genericFunctionInference2.ts, 0, 0)) +>MyState : Symbol(MyState, Decl(genericFunctionInference2.ts, 3, 93)) + +const myReducer1: Reducer = combineReducers({ +>myReducer1 : Symbol(myReducer1, Decl(genericFunctionInference2.ts, 8, 5)) +>Reducer : Symbol(Reducer, Decl(genericFunctionInference2.ts, 0, 0)) +>MyState : Symbol(MyState, Decl(genericFunctionInference2.ts, 3, 93)) +>combineReducers : Symbol(combineReducers, Decl(genericFunctionInference2.ts, 2, 34)) + + combined: combineReducers({ foo }), +>combined : Symbol(combined, Decl(genericFunctionInference2.ts, 8, 54)) +>combineReducers : Symbol(combineReducers, Decl(genericFunctionInference2.ts, 2, 34)) +>foo : Symbol(foo, Decl(genericFunctionInference2.ts, 9, 31)) + +}); + +const myReducer2 = combineReducers({ +>myReducer2 : Symbol(myReducer2, Decl(genericFunctionInference2.ts, 12, 5)) +>combineReducers : Symbol(combineReducers, Decl(genericFunctionInference2.ts, 2, 34)) + + combined: combineReducers({ foo }), +>combined : Symbol(combined, Decl(genericFunctionInference2.ts, 12, 36)) +>combineReducers : Symbol(combineReducers, Decl(genericFunctionInference2.ts, 2, 34)) +>foo : Symbol(foo, Decl(genericFunctionInference2.ts, 13, 31)) + +}); + +// Repro from #30942 + +declare function withH(handlerCreators: HandleCreatorsFactory): U; +>withH : Symbol(withH, Decl(genericFunctionInference2.ts, 14, 3)) +>T : Symbol(T, Decl(genericFunctionInference2.ts, 18, 23)) +>U : Symbol(U, Decl(genericFunctionInference2.ts, 18, 25)) +>handlerCreators : Symbol(handlerCreators, Decl(genericFunctionInference2.ts, 18, 29)) +>HandleCreatorsFactory : Symbol(HandleCreatorsFactory, Decl(genericFunctionInference2.ts, 20, 28)) +>T : Symbol(T, Decl(genericFunctionInference2.ts, 18, 23)) +>U : Symbol(U, Decl(genericFunctionInference2.ts, 18, 25)) +>U : Symbol(U, Decl(genericFunctionInference2.ts, 18, 25)) + +type Props = { out: number } +>Props : Symbol(Props, Decl(genericFunctionInference2.ts, 18, 78)) +>out : Symbol(out, Decl(genericFunctionInference2.ts, 20, 14)) + +type HandleCreatorsFactory = (initialProps: T) => { [P in keyof U]: (props: T) => U[P] }; +>HandleCreatorsFactory : Symbol(HandleCreatorsFactory, Decl(genericFunctionInference2.ts, 20, 28)) +>T : Symbol(T, Decl(genericFunctionInference2.ts, 22, 27)) +>U : Symbol(U, Decl(genericFunctionInference2.ts, 22, 29)) +>initialProps : Symbol(initialProps, Decl(genericFunctionInference2.ts, 22, 36)) +>T : Symbol(T, Decl(genericFunctionInference2.ts, 22, 27)) +>P : Symbol(P, Decl(genericFunctionInference2.ts, 22, 59)) +>U : Symbol(U, Decl(genericFunctionInference2.ts, 22, 29)) +>props : Symbol(props, Decl(genericFunctionInference2.ts, 22, 75)) +>T : Symbol(T, Decl(genericFunctionInference2.ts, 22, 27)) +>U : Symbol(U, Decl(genericFunctionInference2.ts, 22, 29)) +>P : Symbol(P, Decl(genericFunctionInference2.ts, 22, 59)) + +const enhancer4 = withH((props: Props) => ({ +>enhancer4 : Symbol(enhancer4, Decl(genericFunctionInference2.ts, 24, 5)) +>withH : Symbol(withH, Decl(genericFunctionInference2.ts, 14, 3)) +>props : Symbol(props, Decl(genericFunctionInference2.ts, 24, 25)) +>Props : Symbol(Props, Decl(genericFunctionInference2.ts, 18, 78)) + + onChange: (props) => (e: any) => {}, +>onChange : Symbol(onChange, Decl(genericFunctionInference2.ts, 24, 44)) +>props : Symbol(props, Decl(genericFunctionInference2.ts, 25, 15)) +>e : Symbol(e, Decl(genericFunctionInference2.ts, 25, 26)) + + onSubmit: (props) => (e: any) => {}, +>onSubmit : Symbol(onSubmit, Decl(genericFunctionInference2.ts, 25, 40)) +>props : Symbol(props, Decl(genericFunctionInference2.ts, 26, 15)) +>e : Symbol(e, Decl(genericFunctionInference2.ts, 26, 26)) + +})); + +enhancer4.onChange(null); +>enhancer4.onChange : Symbol(onChange, Decl(genericFunctionInference2.ts, 24, 44)) +>enhancer4 : Symbol(enhancer4, Decl(genericFunctionInference2.ts, 24, 5)) +>onChange : Symbol(onChange, Decl(genericFunctionInference2.ts, 24, 44)) + diff --git a/tests/baselines/reference/genericFunctionInference2.types b/tests/baselines/reference/genericFunctionInference2.types new file mode 100644 index 0000000000000..75e116d169dd9 --- /dev/null +++ b/tests/baselines/reference/genericFunctionInference2.types @@ -0,0 +1,96 @@ +=== tests/cases/compiler/genericFunctionInference2.ts === +// Repro from #30685 + +type Reducer = (state: S) => S; +>Reducer : Reducer +>state : S + +declare function combineReducers(reducers: { [K in keyof S]: Reducer }): Reducer; +>combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer +>reducers : { [K in keyof S]: Reducer; } + +type MyState = { combined: { foo: number } }; +>MyState : MyState +>combined : { foo: number; } +>foo : number + +declare const foo: Reducer; +>foo : Reducer + +const myReducer1: Reducer = combineReducers({ +>myReducer1 : Reducer +>combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: any; }; }> +>combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer +>{ combined: combineReducers({ foo }),} : { combined: Reducer<{ foo: number; }>; } + + combined: combineReducers({ foo }), +>combined : Reducer<{ foo: number; }> +>combineReducers({ foo }) : Reducer<{ foo: number; }> +>combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer +>{ foo } : { foo: Reducer; } +>foo : Reducer + +}); + +const myReducer2 = combineReducers({ +>myReducer2 : Reducer<{ combined: { foo: any; }; }> +>combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: any; }; }> +>combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer +>{ combined: combineReducers({ foo }),} : { combined: Reducer<{ foo: number; }>; } + + combined: combineReducers({ foo }), +>combined : Reducer<{ foo: number; }> +>combineReducers({ foo }) : Reducer<{ foo: number; }> +>combineReducers : (reducers: { [K in keyof S]: Reducer; }) => Reducer +>{ foo } : { foo: Reducer; } +>foo : Reducer + +}); + +// Repro from #30942 + +declare function withH(handlerCreators: HandleCreatorsFactory): U; +>withH : (handlerCreators: HandleCreatorsFactory) => U +>handlerCreators : HandleCreatorsFactory + +type Props = { out: number } +>Props : Props +>out : number + +type HandleCreatorsFactory = (initialProps: T) => { [P in keyof U]: (props: T) => U[P] }; +>HandleCreatorsFactory : HandleCreatorsFactory +>initialProps : T +>props : T + +const enhancer4 = withH((props: Props) => ({ +>enhancer4 : { onChange: (e: any) => void; onSubmit: (e: any) => void; } +>withH((props: Props) => ({ onChange: (props) => (e: any) => {}, onSubmit: (props) => (e: any) => {},})) : { onChange: (e: any) => void; onSubmit: (e: any) => void; } +>withH : (handlerCreators: HandleCreatorsFactory) => U +>(props: Props) => ({ onChange: (props) => (e: any) => {}, onSubmit: (props) => (e: any) => {},}) : (props: Props) => { onChange: (props: Props) => (e: any) => void; onSubmit: (props: Props) => (e: any) => void; } +>props : Props +>({ onChange: (props) => (e: any) => {}, onSubmit: (props) => (e: any) => {},}) : { onChange: (props: Props) => (e: any) => void; onSubmit: (props: Props) => (e: any) => void; } +>{ onChange: (props) => (e: any) => {}, onSubmit: (props) => (e: any) => {},} : { onChange: (props: Props) => (e: any) => void; onSubmit: (props: Props) => (e: any) => void; } + + onChange: (props) => (e: any) => {}, +>onChange : (props: Props) => (e: any) => void +>(props) => (e: any) => {} : (props: Props) => (e: any) => void +>props : Props +>(e: any) => {} : (e: any) => void +>e : any + + onSubmit: (props) => (e: any) => {}, +>onSubmit : (props: Props) => (e: any) => void +>(props) => (e: any) => {} : (props: Props) => (e: any) => void +>props : Props +>(e: any) => {} : (e: any) => void +>e : any + +})); + +enhancer4.onChange(null); +>enhancer4.onChange(null) : void +>enhancer4.onChange : (e: any) => void +>enhancer4 : { onChange: (e: any) => void; onSubmit: (e: any) => void; } +>onChange : (e: any) => void +>null : null + diff --git a/tests/cases/compiler/genericFunctionInference2.ts b/tests/cases/compiler/genericFunctionInference2.ts new file mode 100644 index 0000000000000..b83247a7781a3 --- /dev/null +++ b/tests/cases/compiler/genericFunctionInference2.ts @@ -0,0 +1,30 @@ +// Repro from #30685 + +type Reducer = (state: S) => S; +declare function combineReducers(reducers: { [K in keyof S]: Reducer }): Reducer; + +type MyState = { combined: { foo: number } }; +declare const foo: Reducer; + +const myReducer1: Reducer = combineReducers({ + combined: combineReducers({ foo }), +}); + +const myReducer2 = combineReducers({ + combined: combineReducers({ foo }), +}); + +// Repro from #30942 + +declare function withH(handlerCreators: HandleCreatorsFactory): U; + +type Props = { out: number } + +type HandleCreatorsFactory = (initialProps: T) => { [P in keyof U]: (props: T) => U[P] }; + +const enhancer4 = withH((props: Props) => ({ + onChange: (props) => (e: any) => {}, + onSubmit: (props) => (e: any) => {}, +})); + +enhancer4.onChange(null);