10000 Infer from generic function return types by ahejlsberg · Pull Request #16072 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content

Infer from generic function return types #16072

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

Merged
merged 12 commits into from
May 26, 2017
Prev Previous commit
Next Next commit
Move return type inference to inferTypeArguments function
  • Loading branch information
ahejlsberg committed May 24, 2017
commit b8d5eff8ace9ee4c6dc46a0c80be126c51983756
49 changes: 24 additions & 25 deletions src/compiler/checker.ts
10000
Original file line number Diff line number Diff line change
Expand Up @@ -7924,7 +7924,7 @@ namespace ts {

function cloneTypeMapper(mapper: TypeMapper): TypeMapper {
return mapper && isInferenceContext(mapper) ?
createInferenceContext(mapper.callNode, mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) :
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) :
mapper;
}

Expand Down Expand Up @@ -10121,11 +10121,10 @@ namespace ts {
}
}

function createInferenceContext(callNode: CallLikeExpression, signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext {
function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext {
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
const context = mapper as InferenceContext;
context.mappedTypes = signature.typeParameters;
context.callNode = callNode;
context.signature = signature;
context.inferences = inferences;
context.flags = flags;
Expand Down Expand Up @@ -10302,15 +10301,15 @@ namespace ts {
// Even if an inference is marked as fixed, we can add candidates from inferences made
// from the return type of generic functions (which only happens when no other candidates
// are present).
if (!inference.isFixed || priority & InferencePriority.ReturnType) {
if (!inference.isFixed) {
if (!inference.candidates || priority < inference.priority) {
inference.candidates = [source];
inference.priority = priority;
}
else if (priority === inference.priority) {
inference.candidates.push(source);
}
if (target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
inference.topLevel = false;
}
}
Expand Down Expand Up @@ -10523,24 +10522,6 @@ namespace ts {
let inferredType = inference.inferredType;
let inferenceSucceeded: boolean;
if (!inferredType) {
if (!inference.candidates && context.callNode && isExpression(context.callNode)) {
// We have no inference candidates. Now attempt to get the contextual type for the call
// expression associated with the context, and if a contextual type is available, infer
// from that type to the return type of the call expression. For example, given a
// 'function wrap<T, U>(cb: (x: T) => U): (x: T) => U' and a call expression
// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type
// of 'f' to the return type of 'wrap'.
const contextualType = getContextualType(context.callNode);
if (contextualType) {
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
// outer call expression. Effectively we just want a snapshot of whatever has been
// inferred for any outer call expression so far.
const mapper = cloneTypeMapper(getContextualMapper(context.callNode));
const instantiatedType = instantiateType(contextualType, mapper);
const returnType = getReturnTypeOfSignature(context.signature);
inferTypes([inference], instantiatedType, returnType, InferencePriority.ReturnType);
}
}
if (inference.candidates) {
// We widen inferred literal types if
// all inferences were made to top-level ocurrences of the type parameter, and
Expand Down Expand Up @@ -14866,7 +14847,7 @@ namespace ts {

// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
const context = createInferenceContext(/*callNode*/ undefined, signature, InferenceFlags.InferUnionTypes);
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
inferTypes(context.inferences, instantiateType(source, contextualMapper), target);
Expand Down Expand Up @@ -14899,6 +14880,24 @@ namespace ts {
context.failedTypeParameterIndex = undefined;
}

// If a contextual type is available, infer from that type to the return type of the call expression. For
// example, given a 'function wrap<T, U>(cb: (x: T) => U): (x: T) => U' and a call expression
// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the
// return type of 'wrap'.
if (isExpression(node)) {
const contextualType = getContextualType(node);
if (contextualType) {
// We clone the contextual mapper to avoid disturbing a resolution in progress for an
// outer call expression. Effectively we just want a snapshot of whatever has been
// inferred for any outer call expression so far.
const mapper = cloneTypeMapper(getContextualMapper(node));
const instantiatedType = instantiateType(contextualType, mapper);
const returnType = getReturnTypeOfSignature(signature);
// Inferences made from return types have lower priority than all other inferences.
inferTypes(context.inferences, instantiatedType, returnType, InferencePriority.ReturnType);
}
}

const thisType = getThisTypeOfSignature(signature);
if (thisType) {
const thisArgumentNode = getThisArgumentOfCall(node);
Expand Down Expand Up @@ -15599,7 +15598,7 @@ namespace ts {
let candidate: Signature;
let typeArgumentsAreValid: boolean;
const inferenceContext = originalCandidate.typeParameters
? createInferenceContext(node, originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : 0)
? createInferenceContext(originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : 0)
: undefined;

while (true) {
Expand Down
1 change: 0 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3361,7 +3361,6 @@ namespace ts {

/* @internal */
export interface InferenceContext extends TypeMapper {
callNode: CallLikeExpression; // Call expression node for which inferences are made
signature: Signature; // Generic signature for which inferences are made
inferences: InferenceInfo[]; // Inferences made for each type parameter
flags: InferenceFlags; // Inference flags
Expand Down
0