Add inference based for 'Promise' based on call to 'resolve'

This commit is contained in:
Ron Buckton 2020-09-09 18:13:33 -07:00
parent 6aec7f4676
commit 56a9871ff3
12 changed files with 507 additions and 95 deletions

View File

@ -19458,7 +19458,7 @@ namespace ts {
source = getUnionType(sources);
}
else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types,
t => !!getInferenceInfoForType(t) || (isGenericMappedType(t) && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) || neverType)))) {
t => !!getInferenceInfoForType(inferences, t) || (isGenericMappedType(t) && !!getInferenceInfoForType(inferences, getHomomorphicTypeVariable(t) || neverType)))) {
// We reduce intersection types only when they contain naked type parameters. For example, when
// inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and
// infer { extra: any } for T. But when inferring to 'string[] & Iterable<T>' we want to keep the
@ -19490,7 +19490,7 @@ namespace ts {
(priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) {
return;
}
const inference = getInferenceInfoForType(target);
const inference = getInferenceInfoForType(inferences, target);
if (inference) {
if (!inference.isFixed) {
if (inference.priority === undefined || priority < inference.priority) {
@ -19687,21 +19687,10 @@ namespace ts {
}
}
function getInferenceInfoForType(type: Type) {
if (type.flags & TypeFlags.TypeVariable) {
for (const inference of inferences) {
if (type === inference.typeParameter) {
return inference;
}
}
}
return undefined;
}
function getSingleTypeVariableFromIntersectionTypes(types: Type[]) {
let typeVariable: Type | undefined;
for (const type of types) {
const t = type.flags & TypeFlags.Intersection && find((<IntersectionType>type).types, t => !!getInferenceInfoForType(t));
const t = type.flags & TypeFlags.Intersection && find((<IntersectionType>type).types, t => !!getInferenceInfoForType(inferences, t));
if (!t || typeVariable && t !== typeVariable) {
return undefined;
}
@ -19722,7 +19711,7 @@ namespace ts {
// equal priority (i.e. of equal quality) to what we would infer for a naked type
// parameter.
for (const t of targets) {
if (getInferenceInfoForType(t)) {
if (getInferenceInfoForType(inferences, t)) {
nakedTypeVariable = t;
typeVariableCount++;
}
@ -19764,7 +19753,7 @@ namespace ts {
// make from nested naked type variables and given slightly higher priority by virtue
// of being first in the candidates array.
for (const t of targets) {
if (getInferenceInfoForType(t)) {
if (getInferenceInfoForType(inferences, t)) {
typeVariableCount++;
}
else {
@ -19778,7 +19767,7 @@ namespace ts {
// we only infer to single naked type variables.
if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
for (const t of targets) {
if (getInferenceInfoForType(t)) {
if (getInferenceInfoForType(inferences, t)) {
inferWithPriority(source, t, InferencePriority.NakedTypeVariable);
}
}
@ -19798,7 +19787,7 @@ namespace ts {
// where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source
// type and then make a secondary inference from that type to T. We make a secondary inference
// such that direct inferences to T get priority over inferences to Partial<T>, for example.
const inference = getInferenceInfoForType((<IndexType>constraintType).type);
const inference = getInferenceInfoForType(inferences, (<IndexType>constraintType).type);
if (inference && !inference.isFixed && !isFromInferenceBlockedSource(source)) {
const inferredType = inferTypeForHomomorphicMappedType(source, target, <IndexType>constraintType);
if (inferredType) {
@ -19909,7 +19898,7 @@ namespace ts {
const middleLength = targetArity - startLength - endLength;
if (middleLength === 2 && elementFlags[startLength] & elementFlags[startLength + 1] & ElementFlags.Variadic && isTupleType(source)) {
// Middle of target is [...T, ...U] and source is tuple type
const targetInfo = getInferenceInfoForType(elementTypes[startLength]);
const targetInfo = getInferenceInfoForType(inferences, elementTypes[startLength]);
if (targetInfo && targetInfo.impliedArity !== undefined) {
// Infer slices from source based on implied arity of T.
inferFromTypes(sliceTupleType(source, startLength, sourceEndLength + sourceArity - targetInfo.impliedArity), elementTypes[startLength]);
@ -20004,6 +19993,22 @@ namespace ts {
}
}
function getInferenceInfoForType(inferences: InferenceInfo[], type: Type) {
if (type.flags & TypeFlags.TypeVariable) {
for (const inference of inferences) {
if (type === inference.typeParameter) {
return inference;
}
}
}
return undefined;
}
function hasHigherPriorityInference(inferences: InferenceInfo[], type: Type, priority: InferencePriority) {
const inference = getInferenceInfoForType(inferences, type);
return !!inference && (inference.isFixed || inference.priority !== undefined && inference.priority < priority);
}
function isTypeOrBaseIdenticalTo(s: Type, t: Type) {
return isTypeIdenticalTo(s, t) || !!(t.flags & TypeFlags.String && s.flags & TypeFlags.StringLiteral || t.flags & TypeFlags.Number && s.flags & TypeFlags.NumberLiteral);
}
@ -20661,7 +20666,7 @@ namespace ts {
}
function isTypeSubsetOf(source: Type, target: Type) {
return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, <UnionType>target);
return source === target || !!(target.flags & TypeFlags.Union) && isTypeSubsetOfUnion(source, <UnionType>target);
}
function isTypeSubsetOfUnion(source: Type, target: UnionType) {
@ -26020,6 +26025,75 @@ namespace ts {
inferTypes(context.inferences, spreadType, restType);
}
// Attempt to solve for `T` in `new Promise<T>(resolve => resolve(t))` (also known as the "revealing constructor" pattern).
// To avoid too much complexity, we use a very restrictive heuristic:
// - Restrict to NewExpression to reduce overhead.
// - `signature` has a single parameter (`callbackType`)
// - `callbackType` has a single call signature (`callbackSignature`) (i.e., `executor: (resolve: (value: T | PromiseLike<T>) => void) => void`)
// - `callbackSignature` has at least one parameter (`innerCallbackType`)
// - `innerCallbackType` has a single call signature (`innerCallbackSignature`) (i.e., `resolve: (value: T | PromiseLike<T>) => void`)
// - `innerCallbackSignature` has a single parameter (`innerCallbackValueType`)
// - `innerCallbackValueType` contains type variable for which we are gathering inferences (i.e. `value: T | PromiseLike<T>`)
// - The function (`callbackFunc`) passed as the argument to the parameter `callbackType` must be inline (i.e., an arrow function or function expression)
// - `callbackFunc` must have one parameter (`innerCallbackParam`) that is untyped (and thus would be contextually typed by `innerCallbackType`)
// If the above conditions are met then:
// - Determine the name in function `callbackFunc` given to the parameter `innerCallbackParam`
// - Find all references to that name in the body of the function `callbackFunc`
// - If `innerCallbackParam` is called directly, collect inferences for the type of the argument passed to the parameter (`innerCallbackValueType`) each call to `innerCallbackParam`
// - If `innerCallbackParam` is passed as the argument to another function, we can attempt to use the contextual type of that parameter for inference.
if (isNewExpression(node) && argCount === 1) {
const callbackType = getTypeAtPosition(signature, 0); // executor: ...
const callbackSignature = getSingleCallSignature(callbackType); // (resolve: (...) => ...) => ...
const callbackFunc = skipParentheses(args[0]);
if (callbackSignature && isFunctionExpressionOrArrowFunction(callbackFunc)) {
const sourceFile = getSourceFileOfNode(callbackFunc);
for (let callbackParamIndex = 0; callbackParamIndex < callbackFunc.parameters.length; callbackParamIndex++) {
const innerCallbackType = tryGetTypeAtPosition(callbackSignature, callbackParamIndex); // resolve: ...
const innerCallbackSignature = innerCallbackType && getSingleCallSignature(innerCallbackType); // (value: T | PromiseLike<T>) => ...
const innerCallbackParam = callbackFunc.parameters[callbackParamIndex];
if (innerCallbackSignature && getParameterCount(innerCallbackSignature) === 1 && isIdentifier(innerCallbackParam.name) && !getEffectiveTypeAnnotationNode(innerCallbackParam)) {
const innerCallbackValueType = getTypeAtPosition(innerCallbackSignature, 0); // value: ...
// Don't do the work if we already have a higher-priority inference.
if (some(signature.typeParameters, typeParam => isTypeSubsetOf(typeParam, innerCallbackValueType) && !hasHigherPriorityInference(context.inferences, typeParam, InferencePriority.RevealingConstructor))) {
const innerCallbackSymbol = getSymbolOfNode(innerCallbackParam);
const positions = getPossibleSymbolReferencePositions(sourceFile, idText(innerCallbackParam.name), callbackFunc);
if (positions.length) {
const candidateReferences = findNodesAtPositions(callbackFunc, positions, sourceFile);
if (candidateReferences.length) {
// The callback will not have a type associated with it, so we temporarily assign it `anyFunctionType` so that
// we do not trigger implicit `any` errors and so that we do not create inferences from it.
const links = getSymbolLinks(innerCallbackSymbol);
const savedType = links.type;
links.type = anyFunctionType;
// collect types for inferences to ppB
for (const candidateReference of candidateReferences) {
if (!isIdentifier(candidateReference) || candidateReference === innerCallbackParam.name) continue;
const candidateReferenceSymbol = resolveName(candidateReference, candidateReference.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
if (candidateReferenceSymbol !== innerCallbackSymbol) continue;
if (isCallExpression(candidateReference.parent) && candidateReference === candidateReference.parent.expression) {
const argType =
candidateReference.parent.arguments.length >= 1 ? checkExpression(candidateReference.parent.arguments[0]) :
voidType;
inferTypes(context.inferences, argType, innerCallbackValueType, InferencePriority.RevealingConstructor);
}
else if (isCallOrNewExpression(candidateReference.parent) && contains(candidateReference.parent.arguments, candidateReference)) {
const callbackType = getContextualType(candidateReference);
const callbackSignature = callbackType && getSingleCallSignature(callbackType);
const callbackParamType = callbackSignature && tryGetTypeAtPosition(callbackSignature, 0);
if (callbackParamType) {
inferTypes(context.inferences, callbackParamType, innerCallbackValueType, InferencePriority.RevealingConstructor);
}
}
}
links.type = savedType;
}
}
}
}
}
}
}
return getInferredTypes(context);
}

View File

@ -5437,10 +5437,11 @@ namespace ts {
ReturnType = 1 << 6, // Inference made from return type of generic function
LiteralKeyof = 1 << 7, // Inference made from a string literal to a keyof T
NoConstraints = 1 << 8, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 9, // Always use strict rules for contravariant inferences
MaxValue = 1 << 10, // Seed for inference priority tracking
RevealingConstructor = 1 << 9, // Inference made to a callback in a "revealing constructor" (i.e., `new Promise(resolve => resolve(1))`)
AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences
MaxValue = 1 << 11, // Seed for inference priority tracking
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof | RevealingConstructor, // These priorities imply that the resulting type should be a combination of all candidates
Circularity = -1, // Inference circularity (value less than all other priorities)
}

View File

@ -6903,4 +6903,77 @@ namespace ts {
return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child);
}
}
export function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile) {
const positions: number[] = [];
/// TODO: Cache symbol existence for files to save text search
// Also, need to make this work for unicode escapes.
// Be resilient in the face of a symbol with no name or zero length name
if (!symbolName || !symbolName.length) {
return positions as readonly number[] as SortedReadonlyArray<number>;
}
const text = sourceFile.text;
const sourceLength = text.length;
const symbolNameLength = symbolName.length;
let position = text.indexOf(symbolName, container.pos);
while (position >= 0) {
// If we are past the end, stop looking
if (position > container.end) break;
// We found a match. Make sure it's not part of a larger word (i.e. the char
// before and after it have to be a non-identifier char).
const endPosition = position + symbolNameLength;
if ((position === 0 || !isIdentifierPart(text.charCodeAt(position - 1), ScriptTarget.Latest)) &&
(endPosition === sourceLength || !isIdentifierPart(text.charCodeAt(endPosition), ScriptTarget.Latest))) {
// Found a real match. Keep searching.
positions.push(position);
}
position = text.indexOf(symbolName, position + symbolNameLength + 1);
}
return positions as readonly number[] as SortedReadonlyArray<number>;
}
export function findNodesAtPositions(container: Node, positions: SortedReadonlyArray<number>, sourceFile = getSourceFileOfNode(container)) {
let i = 0;
const results: Node[] = [];
visit(container);
return results;
function visit(node: Node) {
const startPos = skipTrivia(sourceFile.text, node.pos);
while (i < positions.length) {
const pos = positions[i];
const startOffset = i;
if (pos >= node.pos && pos < node.end) {
if (pos < startPos) {
// The position exists in the node's trivia, so we should skip it and
// move on to the next position
i++;
}
else {
const length = results.length;
forEachChild(node, visit);
if (length === results.length) {
// no children were added, so add this node
results.push(node);
// advance to the next position
i++;
}
}
}
else {
// If we've advanced past the end of our parent we should break out of
// the containing `forEachChild`. Otherwise, the position is not contained
// within this node so we should skip to the next node
return !!node.parent && pos > node.parent.end;
}
Debug.assert(i !== startOffset, "Position did not advance");
}
}
}
}

View File

@ -1261,41 +1261,6 @@ namespace ts.FindAllReferences {
return getPossibleSymbolReferencePositions(sourceFile, symbolName, container).map(pos => getTouchingPropertyName(sourceFile, pos));
}
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): readonly number[] {
const positions: number[] = [];
/// TODO: Cache symbol existence for files to save text search
// Also, need to make this work for unicode escapes.
// Be resilient in the face of a symbol with no name or zero length name
if (!symbolName || !symbolName.length) {
return positions;
}
const text = sourceFile.text;
const sourceLength = text.length;
const symbolNameLength = symbolName.length;
let position = text.indexOf(symbolName, container.pos);
while (position >= 0) {
// If we are past the end, stop looking
if (position > container.end) break;
// We found a match. Make sure it's not part of a larger word (i.e. the char
// before and after it have to be a non-identifier char).
const endPosition = position + symbolNameLength;
if ((position === 0 || !isIdentifierPart(text.charCodeAt(position - 1), ScriptTarget.Latest)) &&
(endPosition === sourceLength || !isIdentifierPart(text.charCodeAt(endPosition), ScriptTarget.Latest))) {
// Found a real match. Keep searching.
positions.push(position);
}
position = text.indexOf(symbolName, position + symbolNameLength + 1);
}
return positions;
}
function getLabelReferencesInNode(container: Node, targetLabel: Identifier): SymbolAndEntries[] {
const sourceFile = container.getSourceFile();
const labelName = targetLabel.text;

View File

@ -2651,9 +2651,10 @@ declare namespace ts {
ReturnType = 64,
LiteralKeyof = 128,
NoConstraints = 256,
AlwaysStrict = 512,
MaxValue = 1024,
PriorityImpliesCombination = 208,
RevealingConstructor = 512,
AlwaysStrict = 1024,
MaxValue = 2048,
PriorityImpliesCombination = 720,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */

View File

@ -2651,9 +2651,10 @@ declare namespace ts {
ReturnType = 64,
LiteralKeyof = 128,
NoConstraints = 256,
AlwaysStrict = 512,
MaxValue = 1024,
PriorityImpliesCombination = 208,
RevealingConstructor = 512,
AlwaysStrict = 1024,
MaxValue = 2048,
PriorityImpliesCombination = 720,
Circularity = -1
}
/** @deprecated Use FileExtensionInfo instead. */

View File

@ -1,21 +1,21 @@
=== tests/cases/conformance/es6/modules/a.ts ===
const x = new Promise( ( resolve, reject ) => { resolve( {} ); } );
>x : Promise<unknown>
>new Promise( ( resolve, reject ) => { resolve( {} ); } ) : Promise<unknown>
>x : Promise<{}>
>new Promise( ( resolve, reject ) => { resolve( {} ); } ) : Promise<{}>
>Promise : PromiseConstructor
>( resolve, reject ) => { resolve( {} ); } : (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void
>resolve : (value: unknown) => void
>( resolve, reject ) => { resolve( {} ); } : (resolve: (value: {} | PromiseLike<{}>) => void, reject: (reason?: any) => void) => void
>resolve : (value: {} | PromiseLike<{}>) => void
>reject : (reason?: any) => void
>resolve( {} ) : void
>resolve : (value: unknown) => void
>resolve : (value: {} | PromiseLike<{}>) => void
>{} : {}
export default x;
>x : Promise<unknown>
>x : Promise<{}>
=== tests/cases/conformance/es6/modules/b.ts ===
import x from './a';
>x : Promise<unknown>
>x : Promise<{}>
( async function() {
>( async function() { const value = await x;}() ) : Promise<void>
@ -23,9 +23,9 @@ import x from './a';
>async function() { const value = await x;} : () => Promise<void>
const value = await x;
>value : unknown
>await x : unknown
>x : Promise<unknown>
>value : {}
>await x : {}
>x : Promise<{}>
}() );

View File

@ -1,21 +1,21 @@
=== tests/cases/conformance/es6/modules/a.ts ===
const x = new Promise( ( resolve, reject ) => { resolve( {} ); } );
>x : Promise<unknown>
>new Promise( ( resolve, reject ) => { resolve( {} ); } ) : Promise<unknown>
>x : Promise<{}>
>new Promise( ( resolve, reject ) => { resolve( {} ); } ) : Promise<{}>
>Promise : PromiseConstructor
>( resolve, reject ) => { resolve( {} ); } : (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void
>resolve : (value: unknown) => void
>( resolve, reject ) => { resolve( {} ); } : (resolve: (value: {} | PromiseLike<{}>) => void, reject: (reason?: any) => void) => void
>resolve : (value: {} | PromiseLike<{}>) => void
>reject : (reason?: any) => void
>resolve( {} ) : void
>resolve : (value: unknown) => void
>resolve : (value: {} | PromiseLike<{}>) => void
>{} : {}
export default x;
>x : Promise<unknown>
>x : Promise<{}>
=== tests/cases/conformance/es6/modules/b.ts ===
import x from './a';
>x : Promise<unknown>
>x : Promise<{}>
( async function() {
>( async function() { const value = await x;}() ) : Promise<void>
@ -23,9 +23,9 @@ import x from './a';
>async function() { const value = await x;} : () => Promise<void>
const value = await x;
>value : unknown
>await x : unknown
>x : Promise<unknown>
>value : {}
>await x : {}
>x : Promise<{}>
}() );

View File

@ -29,15 +29,15 @@ export class BrokenClass {
>[] : undefined[]
let populateItems = (order) => {
>populateItems : (order: any) => Promise<unknown>
>(order) => { return new Promise((resolve, reject) => { this.doStuff(order.id) .then((items) => { order.items = items; resolve(order); }); }); } : (order: any) => Promise<unknown>
>populateItems : (order: any) => Promise<any>
>(order) => { return new Promise((resolve, reject) => { this.doStuff(order.id) .then((items) => { order.items = items; resolve(order); }); }); } : (order: any) => Promise<any>
>order : any
return new Promise((resolve, reject) => {
>new Promise((resolve, reject) => { this.doStuff(order.id) .then((items) => { order.items = items; resolve(order); }); }) : Promise<unknown>
>new Promise((resolve, reject) => { this.doStuff(order.id) .then((items) => { order.items = items; resolve(order); }); }) : Promise<any>
>Promise : PromiseConstructor
>(resolve, reject) => { this.doStuff(order.id) .then((items) => { order.items = items; resolve(order); }); } : (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void
>resolve : (value: unknown) => void
>(resolve, reject) => { this.doStuff(order.id) .then((items) => { order.items = items; resolve(order); }); } : (resolve: (value: any) => void, reject: (reason?: any) => void) => void
>resolve : (value: any) => void
>reject : (reason?: any) => void
this.doStuff(order.id)
@ -65,7 +65,7 @@ export class BrokenClass {
resolve(order);
>resolve(order) : void
>resolve : (value: unknown) => void
>resolve : (value: any) => void
>order : any
});
@ -74,19 +74,19 @@ export class BrokenClass {
return Promise.all(result.map(populateItems))
>Promise.all(result.map(populateItems)) .then((orders: Array<MyModule.MyModel>) => { resolve(orders); }) : Promise<void>
>Promise.all(result.map(populateItems)) .then : <TResult1 = unknown[], TResult2 = never>(onfulfilled?: (value: unknown[]) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>Promise.all(result.map(populateItems)) : Promise<unknown[]>
>Promise.all(result.map(populateItems)) .then : <TResult1 = any[], TResult2 = never>(onfulfilled?: (value: any[]) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>Promise.all(result.map(populateItems)) : Promise<any[]>
>Promise.all : { <T>(values: Iterable<T | PromiseLike<T>>): Promise<T[]>; <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; <T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; <T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; <T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; <T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>; <T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>; <T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>]): Promise<[T1, T2, T3, T4]>; <T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>; <T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>; <T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>; }
>Promise : PromiseConstructor
>all : { <T>(values: Iterable<T | PromiseLike<T>>): Promise<T[]>; <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; <T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; <T1, T2, T3, T4, T5, T6, T7, T8>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; <T1, T2, T3, T4, T5, T6, T7>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; <T1, T2, T3, T4, T5, T6>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>]): Promise<[T1, T2, T3, T4, T5, T6]>; <T1, T2, T3, T4, T5>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>, T5 | PromiseLike<T5>]): Promise<[T1, T2, T3, T4, T5]>; <T1, T2, T3, T4>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike<T4>]): Promise<[T1, T2, T3, T4]>; <T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>; <T1, T2>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>; <T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>; }
>result.map(populateItems) : Promise<unknown>[]
>result.map(populateItems) : Promise<any>[]
>result.map : <U>(callbackfn: (value: MyModule.MyModel, index: number, array: MyModule.MyModel[]) => U, thisArg?: any) => U[]
>result : MyModule.MyModel[]
>map : <U>(callbackfn: (value: MyModule.MyModel, index: number, array: MyModule.MyModel[]) => U, thisArg?: any) => U[]
>populateItems : (order: any) => Promise<unknown>
>populateItems : (order: any) => Promise<any>
.then((orders: Array<MyModule.MyModel>) => {
>then : <TResult1 = unknown[], TResult2 = never>(onfulfilled?: (value: unknown[]) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>then : <TResult1 = any[], TResult2 = never>(onfulfilled?: (value: any[]) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
>(orders: Array<MyModule.MyModel>) => { resolve(orders); } : (orders: Array<MyModule.MyModel>) => void
>orders : MyModule.MyModel[]
>MyModule : any

View File

@ -0,0 +1,110 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/revealingConstructorInference.ts ===
// uncalled
const p0 = new Promise(resolve => {});
>p0 : Symbol(p0, Decl(revealingConstructorInference.ts, 1, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 1, 23))
// called with no argument
const p1 = new Promise(resolve => resolve());
>p1 : Symbol(p1, Decl(revealingConstructorInference.ts, 4, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 4, 23))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 4, 23))
// called with argument
const p2 = new Promise(resolve => resolve(1));
>p2 : Symbol(p2, Decl(revealingConstructorInference.ts, 7, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 7, 23))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 7, 23))
// called with promise-like argument
const p3 = new Promise(resolve => resolve(Promise.resolve(1)));
>p3 : Symbol(p3, Decl(revealingConstructorInference.ts, 10, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 10, 23))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 10, 23))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
// called with multiple arguments
const p4 = new Promise(resolve => {
>p4 : Symbol(p4, Decl(revealingConstructorInference.ts, 13, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 13, 23))
resolve(1);
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 13, 23))
resolve("a");
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 13, 23))
});
// called with multiple arguments (mix of non-promise and PromiseLike)
const p5 = new Promise(resolve => {
>p5 : Symbol(p5, Decl(revealingConstructorInference.ts, 19, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 19, 23))
resolve(1);
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 19, 23))
resolve(Promise.resolve("a"));
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 19, 23))
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
});
// called with argument in nested callback
declare function soon(f: () => void): void;
>soon : Symbol(soon, Decl(revealingConstructorInference.ts, 22, 3))
>f : Symbol(f, Decl(revealingConstructorInference.ts, 25, 22))
const p6 = new Promise(resolve => {
>p6 : Symbol(p6, Decl(revealingConstructorInference.ts, 26, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 26, 23))
soon(() => resolve(1));
>soon : Symbol(soon, Decl(revealingConstructorInference.ts, 22, 3))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 26, 23))
});
// callback passed to another function
declare function resolveWith<T>(f: (value: T) => void, value: T): void;
>resolveWith : Symbol(resolveWith, Decl(revealingConstructorInference.ts, 28, 3))
>T : Symbol(T, Decl(revealingConstructorInference.ts, 31, 29))
>f : Symbol(f, Decl(revealingConstructorInference.ts, 31, 32))
>value : Symbol(value, Decl(revealingConstructorInference.ts, 31, 36))
>T : Symbol(T, Decl(revealingConstructorInference.ts, 31, 29))
>value : Symbol(value, Decl(revealingConstructorInference.ts, 31, 54))
>T : Symbol(T, Decl(revealingConstructorInference.ts, 31, 29))
const p7 = new Promise(resolve => resolveWith(resolve, 1));
>p7 : Symbol(p7, Decl(revealingConstructorInference.ts, 32, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 32, 23))
>resolveWith : Symbol(resolveWith, Decl(revealingConstructorInference.ts, 28, 3))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 32, 23))
// lower priority inference
const enum E { zero = 0 }
>E : Symbol(E, Decl(revealingConstructorInference.ts, 32, 59))
>zero : Symbol(E.zero, Decl(revealingConstructorInference.ts, 35, 14))
const p8: Promise<number> = new Promise(resolve => resolve(E.zero));
>p8 : Symbol(p8, Decl(revealingConstructorInference.ts, 36, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 36, 40))
>resolve : Symbol(resolve, Decl(revealingConstructorInference.ts, 36, 40))
>E.zero : Symbol(E.zero, Decl(revealingConstructorInference.ts, 35, 14))
>E : Symbol(E, Decl(revealingConstructorInference.ts, 32, 59))
>zero : Symbol(E.zero, Decl(revealingConstructorInference.ts, 35, 14))

View File

@ -0,0 +1,147 @@
=== tests/cases/conformance/types/typeRelationships/typeInference/revealingConstructorInference.ts ===
// uncalled
const p0 = new Promise(resolve => {});
>p0 : Promise<unknown>
>new Promise(resolve => {}) : Promise<unknown>
>Promise : PromiseConstructor
>resolve => {} : (resolve: (value: unknown) => void) => void
>resolve : (value: unknown) => void
// called with no argument
const p1 = new Promise(resolve => resolve());
>p1 : Promise<void>
>new Promise(resolve => resolve()) : Promise<void>
>Promise : PromiseConstructor
>resolve => resolve() : (resolve: (value: void | PromiseLike<void>) => void) => void
>resolve : (value: void | PromiseLike<void>) => void
>resolve() : void
>resolve : (value: void | PromiseLike<void>) => void
// called with argument
const p2 = new Promise(resolve => resolve(1));
>p2 : Promise<number>
>new Promise(resolve => resolve(1)) : Promise<number>
>Promise : PromiseConstructor
>resolve => resolve(1) : (resolve: (value: number | PromiseLike<number>) => void) => void
>resolve : (value: number | PromiseLike<number>) => void
>resolve(1) : void
>resolve : (value: number | PromiseLike<number>) => void
>1 : 1
// called with promise-like argument
const p3 = new Promise(resolve => resolve(Promise.resolve(1)));
>p3 : Promise<number>
>new Promise(resolve => resolve(Promise.resolve(1))) : Promise<number>
>Promise : PromiseConstructor
>resolve => resolve(Promise.resolve(1)) : (resolve: (value: number | PromiseLike<number>) => void) => any
>resolve : (value: number | PromiseLike<number>) => void
>resolve(Promise.resolve(1)) : void
>resolve : (value: number | PromiseLike<number>) => void
>Promise.resolve(1) : Promise<number>
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
>Promise : PromiseConstructor
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
>1 : 1
// called with multiple arguments
const p4 = new Promise(resolve => {
>p4 : Promise<string | number>
>new Promise(resolve => { resolve(1); resolve("a");}) : Promise<string | number>
>Promise : PromiseConstructor
>resolve => { resolve(1); resolve("a");} : (resolve: (value: string | number | PromiseLike<string | number>) => void) => void
>resolve : (value: string | number | PromiseLike<string | number>) => void
resolve(1);
>resolve(1) : void
>resolve : (value: string | number | PromiseLike<string | number>) => void
>1 : 1
resolve("a");
>resolve("a") : void
>resolve : (value: string | number | PromiseLike<string | number>) => void
>"a" : "a"
});
// called with multiple arguments (mix of non-promise and PromiseLike)
const p5 = new Promise(resolve => {
>p5 : Promise<string | number>
>new Promise(resolve => { resolve(1); resolve(Promise.resolve("a"));}) : Promise<string | number>
>Promise : PromiseConstructor
>resolve => { resolve(1); resolve(Promise.resolve("a"));} : (resolve: (value: string | number | PromiseLike<string | number>) => void) => void
>resolve : (value: string | number | PromiseLike<string | number>) => void
resolve(1);
>resolve(1) : void
>resolve : (value: string | number | PromiseLike<string | number>) => void
>1 : 1
resolve(Promise.resolve("a"));
>resolve(Promise.resolve("a")) : void
>resolve : (value: string | number | PromiseLike<string | number>) => void
>Promise.resolve("a") : Promise<string>
>Promise.resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
>Promise : PromiseConstructor
>resolve : { (): Promise<void>; <T>(value: T | PromiseLike<T>): Promise<T>; }
>"a" : "a"
});
// called with argument in nested callback
declare function soon(f: () => void): void;
>soon : (f: () => void) => void
>f : () => void
const p6 = new Promise(resolve => {
>p6 : Promise<number>
>new Promise(resolve => { soon(() => resolve(1));}) : Promise<number>
>Promise : PromiseConstructor
>resolve => { soon(() => resolve(1));} : (resolve: (value: number | PromiseLike<number>) => void) => void
>resolve : (value: number | PromiseLike<number>) => void
soon(() => resolve(1));
>soon(() => resolve(1)) : void
>soon : (f: () => void) => void
>() => resolve(1) : () => void
>resolve(1) : void
>resolve : (value: number | PromiseLike<number>) => void
>1 : 1
});
// callback passed to another function
declare function resolveWith<T>(f: (value: T) => void, value: T): void;
>resolveWith : <T>(f: (value: T) => void, value: T) => void
>f : (value: T) => void
>value : T
>value : T
const p7 = new Promise(resolve => resolveWith(resolve, 1));
>p7 : Promise<number>
>new Promise(resolve => resolveWith(resolve, 1)) : Promise<number>
>Promise : PromiseConstructor
>resolve => resolveWith(resolve, 1) : (resolve: (value: number | PromiseLike<number>) => void) => void
>resolve : (value: number | PromiseLike<number>) => void
>resolveWith(resolve, 1) : void
>resolveWith : <T>(f: (value: T) => void, value: T) => void
>resolve : (value: number | PromiseLike<number>) => void
>1 : 1
// lower priority inference
const enum E { zero = 0 }
>E : E
>zero : E.zero
>0 : 0
const p8: Promise<number> = new Promise(resolve => resolve(E.zero));
>p8 : Promise<number>
>new Promise(resolve => resolve(E.zero)) : Promise<number>
>Promise : PromiseConstructor
>resolve => resolve(E.zero) : (resolve: (value: number | PromiseLike<number>) => void) => void
>resolve : (value: number | PromiseLike<number>) => void
>resolve(E.zero) : void
>resolve : (value: number | PromiseLike<number>) => void
>E.zero : E
>E : typeof E
>zero : E

View File

@ -0,0 +1,40 @@
// @target: esnext
// @noEmit: true
// uncalled
const p0 = new Promise(resolve => {});
// called with no argument
const p1 = new Promise(resolve => resolve());
// called with argument
const p2 = new Promise(resolve => resolve(1));
// called with promise-like argument
const p3 = new Promise(resolve => resolve(Promise.resolve(1)));
// called with multiple arguments
const p4 = new Promise(resolve => {
resolve(1);
resolve("a");
});
// called with multiple arguments (mix of non-promise and PromiseLike)
const p5 = new Promise(resolve => {
resolve(1);
resolve(Promise.resolve("a"));
});
// called with argument in nested callback
declare function soon(f: () => void): void;
const p6 = new Promise(resolve => {
soon(() => resolve(1));
});
// callback passed to another function
declare function resolveWith<T>(f: (value: T) => void, value: T): void;
const p7 = new Promise(resolve => resolveWith(resolve, 1));
// lower priority inference
const enum E { zero = 0 }
const p8: Promise<number> = new Promise(resolve => resolve(E.zero));