Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a proper non-inferrable type #30856

Merged
merged 4 commits into from Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 15 additions & 25 deletions src/compiler/checker.ts
Expand Up @@ -404,10 +404,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");
Expand All @@ -433,6 +433,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]);
Expand All @@ -453,7 +454,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);
Expand Down Expand Up @@ -2797,14 +2798,9 @@ namespace ts {
return result;
}

function createIntrinsicType(kind: TypeFlags, intrinsicName: string): IntrinsicType {
function createIntrinsicType(kind: TypeFlags, intrinsicName: string, objectFlags: ObjectFlags = 0): IntrinsicType {
const type = <IntrinsicType>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;
}
Expand Down Expand Up @@ -14506,16 +14502,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).
Expand Down Expand Up @@ -14670,7 +14660,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);
Expand Down Expand Up @@ -14991,7 +14981,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);
}
Expand Down Expand Up @@ -21120,7 +21110,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);
Expand Down Expand Up @@ -21603,8 +21593,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) {
Expand Down Expand Up @@ -22411,7 +22401,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;
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/types.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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 */
Expand Down
26 changes: 26 additions & 0 deletions tests/baselines/reference/genericFunctionInference2.js
@@ -0,0 +1,26 @@
//// [genericFunctionInference2.ts]
// Repro from #30685

type Reducer<S> = (state: S) => S;
declare function combineReducers<S>(reducers: { [K in keyof S]: Reducer<S[K]> }): Reducer<S>;

type MyState = { combined: { foo: number } };
declare const foo: Reducer<MyState['combined']['foo']>;

const myReducer1: Reducer<MyState> = combineReducers({
combined: combineReducers({ foo }),
});

const myReducer2 = combineReducers({
combined: combineReducers({ foo }),
});


//// [genericFunctionInference2.js]
// Repro from #30685
var myReducer1 = combineReducers({
combined: combineReducers({ foo: foo })
});
var myReducer2 = combineReducers({
combined: combineReducers({ foo: foo })
});
56 changes: 56 additions & 0 deletions tests/baselines/reference/genericFunctionInference2.symbols
@@ -0,0 +1,56 @@
=== tests/cases/compiler/genericFunctionInference2.ts ===
// Repro from #30685

type Reducer<S> = (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<S>(reducers: { [K in keyof S]: Reducer<S[K]> }): Reducer<S>;
>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<MyState['combined']['foo']>;
>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<MyState> = 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))

});

49 changes: 49 additions & 0 deletions tests/baselines/reference/genericFunctionInference2.types
@@ -0,0 +1,49 @@
=== tests/cases/compiler/genericFunctionInference2.ts ===
// Repro from #30685

type Reducer<S> = (state: S) => S;
>Reducer : Reducer<S>
>state : S

declare function combineReducers<S>(reducers: { [K in keyof S]: Reducer<S[K]> }): Reducer<S>;
>combineReducers : <S>(reducers: { [K in keyof S]: Reducer<S[K]>; }) => Reducer<S>
>reducers : { [K in keyof S]: Reducer<S[K]>; }

type MyState = { combined: { foo: number } };
>MyState : MyState
>combined : { foo: number; }
>foo : number

declare const foo: Reducer<MyState['combined']['foo']>;
>foo : Reducer<number>

const myReducer1: Reducer<MyState> = combineReducers({
>myReducer1 : Reducer<MyState>
>combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: any; }; }>
>combineReducers : <S>(reducers: { [K in keyof S]: Reducer<S[K]>; }) => Reducer<S>
>{ combined: combineReducers({ foo }),} : { combined: Reducer<{ foo: number; }>; }

combined: combineReducers({ foo }),
>combined : Reducer<{ foo: number; }>
>combineReducers({ foo }) : Reducer<{ foo: number; }>
>combineReducers : <S>(reducers: { [K in keyof S]: Reducer<S[K]>; }) => Reducer<S>
>{ foo } : { foo: Reducer<number>; }
>foo : Reducer<number>

});

const myReducer2 = combineReducers({
>myReducer2 : Reducer<{ combined: { foo: any; }; }>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this supposed to be Reducer<{ combined: { foo: number; }; }>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's supposed to be Reducer<{ combined: { foo: Reducer<number> } }>, but we never elaborate more than one level of reverse mapped types, thus the any.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhhhhh, right. I thought we printed those with ... now, rather than any, but that may only be for quickinfo...

>combineReducers({ combined: combineReducers({ foo }),}) : Reducer<{ combined: { foo: any; }; }>
>combineReducers : <S>(reducers: { [K in keyof S]: Reducer<S[K]>; }) => Reducer<S>
>{ combined: combineReducers({ foo }),} : { combined: Reducer<{ foo: number; }>; }

combined: combineReducers({ foo }),
>combined : Reducer<{ foo: number; }>
>combineReducers({ foo }) : Reducer<{ foo: number; }>
>combineReducers : <S>(reducers: { [K in keyof S]: Reducer<S[K]>; }) => Reducer<S>
>{ foo } : { foo: Reducer<number>; }
>foo : Reducer<number>

});

15 changes: 15 additions & 0 deletions tests/cases/compiler/genericFunctionInference2.ts
@@ -0,0 +1,15 @@
// Repro from #30685

type Reducer<S> = (state: S) => S;
declare function combineReducers<S>(reducers: { [K in keyof S]: Reducer<S[K]> }): Reducer<S>;

type MyState = { combined: { foo: number } };
declare const foo: Reducer<MyState['combined']['foo']>;

const myReducer1: Reducer<MyState> = combineReducers({
combined: combineReducers({ foo }),
});

const myReducer2 = combineReducers({
combined: combineReducers({ foo }),
});