Propagate outer type parameters of single signature types (#57403)

This commit is contained in:
Wesley Wigham 2024-03-27 09:57:09 -07:00 committed by GitHub
parent 6d0cc1beda
commit e1874f3ffe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 423 additions and 17 deletions

View File

@ -967,6 +967,7 @@ import {
SignatureFlags,
SignatureKind,
singleElementArray,
SingleSignatureType,
skipOuterExpressions,
skipParentheses,
skipTrivia,
@ -7165,7 +7166,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const abstractSignatures = filter(resolved.constructSignatures, signature => !!(signature.flags & SignatureFlags.Abstract));
if (some(abstractSignatures)) {
const types = map(abstractSignatures, getOrCreateTypeFromSignature);
const types = map(abstractSignatures, s => getOrCreateTypeFromSignature(s));
// count the number of type elements excluding abstract constructors
const typeElementCount = resolved.callSignatures.length +
(resolved.constructSignatures.length - abstractSignatures.length) +
@ -15672,7 +15673,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return undefined;
}
function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature {
function getSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature {
const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript));
if (inferredTypeParameters) {
const returnSignature = getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature));
@ -15736,6 +15737,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
);
}
function getImplementationSignature(signature: Signature) {
return signature.typeParameters ?
signature.implementationSignatureCache ||= createImplementationSignature(signature) :
signature;
}
function createImplementationSignature(signature: Signature) {
return signature.typeParameters ? instantiateSignature(signature, createTypeMapper([], [])) : signature;
}
function getBaseSignature(signature: Signature) {
const typeParameters = signature.typeParameters;
if (typeParameters) {
@ -15757,7 +15768,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return signature;
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
function getOrCreateTypeFromSignature(signature: Signature, outerTypeParameters?: TypeParameter[]): ObjectType {
// There are two ways to declare a construct signature, one is by declaring a class constructor
// using the constructor keyword, and the other is declaring a bare construct signature in an
// object type literal or interface (using the new keyword). Each way of declaring a constructor
@ -15768,7 +15779,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// If declaration is undefined, it is likely to be the signature of the default constructor.
const isConstructor = kind === undefined || kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
const type = createObjectType(ObjectFlags.Anonymous);
// The type must have a symbol with a `Function` flag and a declaration in order to be correctly flagged as possibly containing
// type variables by `couldContainTypeVariables`
const type = createObjectType(ObjectFlags.Anonymous | ObjectFlags.SingleSignatureType, createSymbol(SymbolFlags.Function, InternalSymbolName.Function)) as SingleSignatureType;
if (signature.declaration && !nodeIsSynthesized(signature.declaration)) { // skip synthetic declarations - keeping those around could be bad, since they lack a parent pointer
type.symbol.declarations = [signature.declaration];
type.symbol.valueDeclaration = signature.declaration;
}
outerTypeParameters ||= signature.declaration && getOuterTypeParameters(signature.declaration, /*includeThisTypes*/ true);
type.outerTypeParameters = outerTypeParameters;
type.members = emptySymbols;
type.properties = emptyArray;
type.callSignatures = !isConstructor ? [signature] : emptyArray;
@ -19749,7 +19769,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const links = getNodeLinks(declaration);
const target = type.objectFlags & ObjectFlags.Reference ? links.resolvedType! as DeferredTypeReference :
type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
let typeParameters = links.outerTypeParameters;
let typeParameters = type.objectFlags & ObjectFlags.SingleSignatureType ? (type as SingleSignatureType).outerTypeParameters : links.outerTypeParameters;
if (!typeParameters) {
// The first time an anonymous type is instantiated we compute and store a list of the type
// parameters that are in scope (and therefore potentially referenced). For type literals that
@ -19980,6 +20000,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (type.objectFlags & ObjectFlags.InstantiationExpressionType) {
(result as InstantiationExpressionType).node = (type as InstantiationExpressionType).node;
}
if (type.objectFlags & ObjectFlags.SingleSignatureType) {
(result as SingleSignatureType).outerTypeParameters = (type as SingleSignatureType).outerTypeParameters;
}
result.target = type;
result.mapper = mapper;
result.aliasSymbol = aliasSymbol || type.aliasSymbol;
@ -25263,6 +25286,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const result = !!(type.flags & TypeFlags.Instantiable ||
type.flags & TypeFlags.Object && !isNonGenericTopLevelType(type) && (
objectFlags & ObjectFlags.Reference && ((type as TypeReference).node || some(getTypeArguments(type as TypeReference), couldContainTypeVariables)) ||
objectFlags & ObjectFlags.SingleSignatureType && !!length((type as SingleSignatureType).outerTypeParameters) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType | ObjectFlags.InstantiationExpressionType)
) ||
@ -25622,6 +25646,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
/**
* @returns `true` if `type` has the shape `[T[0]]` where `T` is `typeParameter`
*/
function isTupleOfSelf(typeParameter: TypeParameter, type: Type) {
return isTupleType(type) && getTupleElementType(type, 0) === getIndexedAccessType(typeParameter, getNumberLiteralType(0)) && !getTypeOfPropertyOfType(type, "1" as __String);
}
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority = InferencePriority.None, contravariant = false) {
let bivariant = false;
let propagationType: Type;
@ -25750,6 +25781,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
inference.priority = priority;
}
if (priority === inference.priority) {
// Inferring A to [A[0]] is a zero information inference (it guarantees A becomes its constraint), but oft arises from generic argument list inferences
// By discarding it early, we can allow more fruitful results to be used instead.
if (isTupleOfSelf(inference.typeParameter, candidate)) {
return;
}
// We make contravariant inferences only if we are in a pure contravariant position,
// i.e. only if we have not descended into a bivariant position.
if (contravariant && !bivariant) {
@ -34388,6 +34424,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
checkMode: CheckMode,
reportErrors: boolean,
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
inferenceContext: InferenceContext | undefined,
): readonly Diagnostic[] | undefined {
const errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } = { errors: undefined, skipLogging: true };
if (isJsxOpeningLikeElement(node)) {
@ -34422,7 +34459,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive),
// we obtain the regular type of any object literal arguments because we may not have inferred complete
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType;
const regularArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType;
// If this was inferred under a given inference context, we may need to instantiate the expression type to finish resolving
// the type variables in the expression.
const checkArgType = inferenceContext ? instantiateType(regularArgType, inferenceContext.nonFixingMapper) : regularArgType;
const effectiveCheckArgumentNode = getEffectiveCheckNode(arg);
if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? effectiveCheckArgumentNode : undefined, effectiveCheckArgumentNode, headMessage, containingMessageChain, errorOutputContainer)) {
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors");
@ -34887,7 +34927,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (headMessage) {
chain = chainDiagnosticMessages(chain, headMessage);
}
const diags = getSignatureApplicabilityError(node, args, last, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, () => chain);
const diags = getSignatureApplicabilityError(node, args, last, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, () => chain, /*inferenceContext*/ undefined);
if (diags) {
for (const d of diags) {
if (last.declaration && candidatesForArgumentError.length > 3) {
@ -34909,7 +34949,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
let i = 0;
for (const c of candidatesForArgumentError) {
const chain = () => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Overload_0_of_1_2_gave_the_following_error, i + 1, candidates.length, signatureToString(c));
const diags = getSignatureApplicabilityError(node, args, c, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, chain);
const diags = getSignatureApplicabilityError(node, args, c, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, chain, /*inferenceContext*/ undefined);
if (diags) {
if (diags.length <= min) {
min = diags.length;
@ -34998,7 +35038,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (some(typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
return undefined;
}
if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined, /*inferenceContext*/ undefined)) {
candidatesForArgumentError = [candidate];
return undefined;
}
@ -35006,7 +35046,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
const candidate = candidates[candidateIndex];
let candidate = candidates[candidateIndex];
if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) {
continue;
}
@ -35015,7 +35055,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
let inferenceContext: InferenceContext | undefined;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[] | undefined;
// If we are *inside the body of candidate*, we need to create a clone of `candidate` with differing type parameter identities,
// so our inference results for this call doesn't pollute expression types referencing the outer type parameter!
if (candidate.declaration && findAncestor(node, a => a === candidate.declaration)) {
candidate = getImplementationSignature(candidate);
}
let typeArgumentTypes: readonly Type[] | undefined;
if (some(typeArguments)) {
typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
if (!typeArgumentTypes) {
@ -35024,8 +35069,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
else {
inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext);
inferenceContext = createInferenceContext(candidate.typeParameters!, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
// The resulting type arguments are instantiated with the inference context mapper, as the inferred types may still contain references to the inference context's
// type variables via contextual projection. These are kept generic until all inferences are locked in, so the dependencies expressed can pass constraint checks.
typeArgumentTypes = instantiateTypes(inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext), inferenceContext.nonFixingMapper);
argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal;
}
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters);
@ -35039,7 +35086,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
else {
checkCandidate = candidate;
}
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined, inferenceContext)) {
// Give preference to error candidates that have no rest parameters (as they are more specific)
(candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate);
continue;
@ -35050,7 +35097,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// round of type inference and applicability checking for this particular candidate.
argCheckMode = CheckMode.Normal;
if (inferenceContext) {
const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext);
const typeArgumentTypes = instantiateTypes(inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext), inferenceContext.mapper);
checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters);
// If the original signature has a generic rest type, instantiation may produce a
// signature with different arity and we need to perform another arity check.
@ -35059,7 +35106,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
continue;
}
}
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) {
if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined, inferenceContext)) {
// Give preference to error candidates that have no rest parameters (as they are more specific)
(candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate);
continue;
@ -39515,7 +39562,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
}
return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, context));
// TODO: The signature may reference any outer inference contexts, but we map pop off and then apply new inference contexts, and thus get different inferred types.
// That this is cached on the *first* such attempt is not currently an issue, since expression types *also* get cached on the first pass. If we ever properly speculate, though,
// the cached "isolatedSignatureType" signature field absolutely needs to be included in the list of speculative caches.
return getOrCreateTypeFromSignature(instantiateSignatureInContextOf(signature, contextualSignature, context), flatMap(inferenceContexts, c => c && map(c.inferences, i => i.typeParameter)).slice());
}
}
}

View File

@ -6221,6 +6221,7 @@ export const enum ObjectFlags {
ContainsSpread = 1 << 21, // Object literal contains spread operation
ObjectRestType = 1 << 22, // Originates in object rest declaration
InstantiationExpressionType = 1 << 23, // Originates in instantiation expression
SingleSignatureType = 1 << 27, // A single signature type extracted from a potentially broader type
/** @internal */
IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type
// Flags that require TypeFlags.Object and ObjectFlags.Reference
@ -6431,6 +6432,12 @@ export interface AnonymousType extends ObjectType {
instantiations?: Map<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
}
/** @internal */
// A SingleSignatureType may have bespoke outer type parameters to handle free type variable inferences
export interface SingleSignatureType extends AnonymousType {
outerTypeParameters?: TypeParameter[];
}
/** @internal */
export interface InstantiationExpressionType extends AnonymousType {
node: NodeWithTypeArguments;
@ -6716,6 +6723,8 @@ export interface Signature {
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/** @internal */
instantiations?: Map<string, Signature>; // Generic signature instantiation cache
/** @internal */
implementationSignatureCache?: Signature; // Copy of the signature with fresh type parameters to use in checking the body of a potentially self-referential generic function (deferred)
}
export const enum IndexKind {

View File

@ -6598,6 +6598,7 @@ declare namespace ts {
ContainsSpread = 2097152,
ObjectRestType = 4194304,
InstantiationExpressionType = 8388608,
SingleSignatureType = 134217728,
}
interface ObjectType extends Type {
objectFlags: ObjectFlags;

View File

@ -0,0 +1,52 @@
//// [tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts] ////
//// [genericCallWithinOwnBodyCastTypeParameterIdentity.ts]
interface Thenable<Value> {
then<V>(
onFulfilled: (value: Value) => V | Thenable<V>,
): Thenable<V>;
}
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
(input: Input): Thenable<Result> => {
const result = fn(input)
return {
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
return toThenable<V, Result>(onFulfilled)(result as Result)
}
};
}
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
(input: Input): Thenable<Result> => {
const result = fn(input)
return {
then(onFulfilled) {
return toThenableInferred(onFulfilled)(result as Result)
}
};
}
//// [genericCallWithinOwnBodyCastTypeParameterIdentity.js]
"use strict";
var toThenable = function (fn) {
return function (input) {
var result = fn(input);
return {
then: function (onFulfilled) {
return toThenable(onFulfilled)(result);
}
};
};
};
var toThenableInferred = function (fn) {
return function (input) {
var result = fn(input);
return {
then: function (onFulfilled) {
return toThenableInferred(onFulfilled)(result);
}
};
};
};

View File

@ -0,0 +1,104 @@
//// [tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts] ////
=== genericCallWithinOwnBodyCastTypeParameterIdentity.ts ===
interface Thenable<Value> {
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>Value : Symbol(Value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 19))
then<V>(
>then : Symbol(Thenable.then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 27))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
onFulfilled: (value: Value) => V | Thenable<V>,
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 12))
>value : Symbol(value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 2, 22))
>Value : Symbol(Value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 19))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
): Thenable<V>;
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 1, 9))
}
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
>toThenable : Symbol(toThenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 5))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 27))
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 35))
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 40))
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 27))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
(input: Input): Thenable<Result> => {
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 7, 5))
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 27))
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
const result = fn(input)
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 8, 13))
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 35))
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 7, 5))
return {
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
>then : Symbol(then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 9, 16))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 20))
>value : Symbol(value, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 34))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
return toThenable<V, Result>(onFulfilled)(result as Result)
>toThenable : Symbol(toThenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 5))
>V : Symbol(V, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 17))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 10, 20))
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 8, 13))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 6, 20))
}
};
}
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
>toThenableInferred : Symbol(toThenableInferred, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 5))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 35))
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 43))
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 48))
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 35))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
(input: Input): Thenable<Result> => {
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 17, 5))
>Input : Symbol(Input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 35))
>Thenable : Symbol(Thenable, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 0, 0))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
const result = fn(input)
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 18, 13))
>fn : Symbol(fn, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 43))
>input : Symbol(input, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 17, 5))
return {
then(onFulfilled) {
>then : Symbol(then, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 19, 16))
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 20, 17))
return toThenableInferred(onFulfilled)(result as Result)
>toThenableInferred : Symbol(toThenableInferred, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 5))
>onFulfilled : Symbol(onFulfilled, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 20, 17))
>result : Symbol(result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 18, 13))
>Result : Symbol(Result, Decl(genericCallWithinOwnBodyCastTypeParameterIdentity.ts, 16, 28))
}
};
}

View File

@ -0,0 +1,83 @@
//// [tests/cases/compiler/genericCallWithinOwnBodyCastTypeParameterIdentity.ts] ////
=== genericCallWithinOwnBodyCastTypeParameterIdentity.ts ===
interface Thenable<Value> {
then<V>(
>then : <V>(onFulfilled: (value: Value) => V | Thenable<V>) => Thenable<V>
onFulfilled: (value: Value) => V | Thenable<V>,
>onFulfilled : (value: Value) => V | Thenable<V>
>value : Value
): Thenable<V>;
}
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
>toThenable : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
><Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input): Thenable<Result> => { const result = fn(input) return { then<V>(onFulfilled: (value: Result) => V | Thenable<V>) { return toThenable<V, Result>(onFulfilled)(result as Result) } }; } : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
>fn : (input: Input) => Result | Thenable<Result>
>input : Input
(input: Input): Thenable<Result> => {
>(input: Input): Thenable<Result> => { const result = fn(input) return { then<V>(onFulfilled: (value: Result) => V | Thenable<V>) { return toThenable<V, Result>(onFulfilled)(result as Result) } }; } : (input: Input) => Thenable<Result>
>input : Input
const result = fn(input)
>result : Result | Thenable<Result>
>fn(input) : Result | Thenable<Result>
>fn : (input: Input) => Result | Thenable<Result>
>input : Input
return {
>{ then<V>(onFulfilled: (value: Result) => V | Thenable<V>) { return toThenable<V, Result>(onFulfilled)(result as Result) } } : { then<V>(onFulfilled: (value: Result) => V | Thenable<V>): Thenable<V>; }
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
>then : <V>(onFulfilled: (value: Result) => V | Thenable<V>) => Thenable<V>
>onFulfilled : (value: Result) => V | Thenable<V>
>value : Result
return toThenable<V, Result>(onFulfilled)(result as Result)
>toThenable<V, Result>(onFulfilled)(result as Result) : Thenable<V>
>toThenable<V, Result>(onFulfilled) : (input: Result) => Thenable<V>
>toThenable : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
>onFulfilled : (value: Result) => V | Thenable<V>
>result as Result : Result
>result : Result | Thenable<Result>
}
};
}
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
>toThenableInferred : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
><Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } }; } : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
>fn : (input: Input) => Result | Thenable<Result>
>input : Input
(input: Input): Thenable<Result> => {
>(input: Input): Thenable<Result> => { const result = fn(input) return { then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } }; } : (input: Input) => Thenable<Result>
>input : Input
const result = fn(input)
>result : Result | Thenable<Result>
>fn(input) : Result | Thenable<Result>
>fn : (input: Input) => Result | Thenable<Result>
>input : Input
return {
>{ then(onFulfilled) { return toThenableInferred(onFulfilled)(result as Result) } } : { then<V>(onFulfilled: (value: Result) => V | Thenable<V>): Thenable<V>; }
then(onFulfilled) {
>then : <V>(onFulfilled: (value: Result) => V | Thenable<V>) => Thenable<V>
>onFulfilled : (value: Result) => V | Thenable<V>
return toThenableInferred(onFulfilled)(result as Result)
>toThenableInferred(onFulfilled)(result as Result) : Thenable<V>
>toThenableInferred(onFulfilled) : (input: Result) => Thenable<V>
>toThenableInferred : <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) => (input: Input) => Thenable<Result>
>onFulfilled : (value: Result) => V | Thenable<V>
>result as Result : Result
>result : Result | Thenable<Result>
}
};
}

View File

@ -0,0 +1,14 @@
//// [tests/cases/compiler/nestedGenericSpreadInference.ts] ////
//// [nestedGenericSpreadInference.ts]
declare function wrap<X>(x: X): { x: X };
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
// This should be of type `number` - ideally, it also would not error.
const leak = call(wrap(<T>(x: T) => x), 1);
//// [nestedGenericSpreadInference.js]
"use strict";
// This should be of type `number` - ideally, it also would not error.
var leak = call(wrap(function (x) { return x; }), 1);

View File

@ -0,0 +1,34 @@
//// [tests/cases/compiler/nestedGenericSpreadInference.ts] ////
=== nestedGenericSpreadInference.ts ===
declare function wrap<X>(x: X): { x: X };
>wrap : Symbol(wrap, Decl(nestedGenericSpreadInference.ts, 0, 0))
>X : Symbol(X, Decl(nestedGenericSpreadInference.ts, 0, 22))
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 0, 25))
>X : Symbol(X, Decl(nestedGenericSpreadInference.ts, 0, 22))
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 0, 33))
>X : Symbol(X, Decl(nestedGenericSpreadInference.ts, 0, 22))
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
>call : Symbol(call, Decl(nestedGenericSpreadInference.ts, 0, 41))
>A : Symbol(A, Decl(nestedGenericSpreadInference.ts, 1, 22))
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 1, 42))
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 1, 46))
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 1, 50))
>args : Symbol(args, Decl(nestedGenericSpreadInference.ts, 1, 55))
>A : Symbol(A, Decl(nestedGenericSpreadInference.ts, 1, 22))
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 1, 42))
>args : Symbol(args, Decl(nestedGenericSpreadInference.ts, 1, 74))
>A : Symbol(A, Decl(nestedGenericSpreadInference.ts, 1, 22))
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 1, 42))
// This should be of type `number` - ideally, it also would not error.
const leak = call(wrap(<T>(x: T) => x), 1);
>leak : Symbol(leak, Decl(nestedGenericSpreadInference.ts, 4, 5))
>call : Symbol(call, Decl(nestedGenericSpreadInference.ts, 0, 41))
>wrap : Symbol(wrap, Decl(nestedGenericSpreadInference.ts, 0, 0))
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 4, 24))
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 4, 27))
>T : Symbol(T, Decl(nestedGenericSpreadInference.ts, 4, 24))
>x : Symbol(x, Decl(nestedGenericSpreadInference.ts, 4, 27))

View File

@ -0,0 +1,27 @@
//// [tests/cases/compiler/nestedGenericSpreadInference.ts] ////
=== nestedGenericSpreadInference.ts ===
declare function wrap<X>(x: X): { x: X };
>wrap : <X>(x: X) => { x: X;}
>x : X
>x : X
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
>call : <A extends unknown[], T>(x: { x: (...args: A) => T; }, ...args: A) => T
>x : { x: (...args: A) => T; }
>x : (...args: A) => T
>args : A
>args : A
// This should be of type `number` - ideally, it also would not error.
const leak = call(wrap(<T>(x: T) => x), 1);
>leak : number
>call(wrap(<T>(x: T) => x), 1) : number
>call : <A extends unknown[], T>(x: { x: (...args: A) => T; }, ...args: A) => T
>wrap(<T>(x: T) => x) : { x: (x: A[0]) => A[0]; }
>wrap : <X>(x: X) => { x: X; }
><T>(x: T) => x : <T>(x: T) => T
>x : T
>x : T
>1 : 1

View File

@ -0,0 +1,26 @@
// @strict: true
interface Thenable<Value> {
then<V>(
onFulfilled: (value: Value) => V | Thenable<V>,
): Thenable<V>;
}
const toThenable = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
(input: Input): Thenable<Result> => {
const result = fn(input)
return {
then<V>(onFulfilled: (value: Result) => V | Thenable<V>) {
return toThenable<V, Result>(onFulfilled)(result as Result)
}
};
}
const toThenableInferred = <Result, Input>(fn: (input: Input) => Result | Thenable<Result>) =>
(input: Input): Thenable<Result> => {
const result = fn(input)
return {
then(onFulfilled) {
return toThenableInferred(onFulfilled)(result as Result)
}
};
}

View File

@ -0,0 +1,6 @@
// @strict: true
declare function wrap<X>(x: X): { x: X };
declare function call<A extends unknown[], T>(x: { x: (...args: A) => T }, ...args: A): T;
// This should be of type `number` - ideally, it also would not error.
const leak = call(wrap(<T>(x: T) => x), 1);