Port fixes from master into release-2.5 (#18361)

* Test: parsing of two-line @typedef jsdoc

* Fix forEachChild's visit of JSDocTypedefTag

Also remove JSDocTypeLiteral.jsdocTypeTag, which made no sense since it
was only useful when storing information for its parent `@typedef` tag.

* Update baselines

* Inline variable to aid control flow

* Improved caching scheme for anonymous types

* Fix signature help

* Optimize caching of type literals

* Accept new baselines

* Fix linting errors

* Use canonicalized forms when comparing signatures

* Minor changes

* Document ThrottledOperations.schedule

* Limit the number of unanswered typings installer requests

If we send them all at once, we (apparently) hit a buffer limit in the
node IPC channel and both TS Server and the typings installer become
unresponsive.

* Fix lint issues

* Add explanatory comment
This commit is contained in:
Mohamed Hegazy 2017-09-09 12:54:30 -07:00 committed by GitHub
parent c476a08188
commit 1c7a29eeb3
16 changed files with 263 additions and 245 deletions

View File

@ -4788,22 +4788,39 @@ namespace ts {
return typeParameters;
}
// Appends the outer type parameters of a node to a set of type parameters and returns the resulting set. The function
// allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set in-place and
// returns the same array.
function appendOuterTypeParameters(typeParameters: TypeParameter[], node: Node): TypeParameter[] {
// Return the outer type parameters of a node or undefined if the node has no outer type parameters.
function getOuterTypeParameters(node: Node, includeThisTypes?: boolean): TypeParameter[] {
while (true) {
node = node.parent;
if (!node) {
return typeParameters;
return undefined;
}
if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression ||
node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression ||
node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.ArrowFunction) {
const declarations = (<ClassLikeDeclaration | FunctionLikeDeclaration>node).typeParameters;
if (declarations) {
return appendTypeParameters(appendOuterTypeParameters(typeParameters, node), declarations);
}
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.JSDocTemplateTag:
case SyntaxKind.MappedType:
const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes);
if (node.kind === SyntaxKind.MappedType) {
return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode((<MappedTypeNode>node).typeParameter)));
}
const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(<DeclarationWithTypeParameters>node) || emptyArray);
const thisType = includeThisTypes &&
(node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration) &&
getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters;
}
}
}
@ -4811,7 +4828,7 @@ namespace ts {
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] {
const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
return appendOuterTypeParameters(/*typeParameters*/ undefined, declaration);
return getOuterTypeParameters(declaration);
}
// The local type parameters are the combined set of type parameters from all declarations of the class,
@ -6614,11 +6631,30 @@ namespace ts {
}
function getErasedSignature(signature: Signature): Signature {
if (!signature.typeParameters) return signature;
if (!signature.erasedSignatureCache) {
signature.erasedSignatureCache = instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true);
}
return signature.erasedSignatureCache;
return signature.typeParameters ?
signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) :
signature;
}
function createErasedSignature(signature: Signature) {
// Create an instantiation of the signature where all type arguments are the any type.
return instantiateSignature(signature, createTypeEraser(signature.typeParameters), /*eraseTypeParameters*/ true);
}
function getCanonicalSignature(signature: Signature): Signature {
return signature.typeParameters ?
signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) :
signature;
}
function createCanonicalSignature(signature: Signature) {
// Create an instantiation of the signature where each unconstrained type parameter is replaced with
// its original. When a generic class or interface is instantiated, each generic method in the class or
// interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios
// where different generations of the same type parameter are in scope). This leads to a lot of new type
// identities, and potentially a lot of work comparing those identities, so here we create an instantiation
// that uses the original type identities for all unconstrained type parameters.
return getSignatureInstantiation(signature, map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp));
}
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
@ -6799,7 +6835,7 @@ namespace ts {
const id = getTypeListId(typeArguments);
let instantiation = links.instantiations.get(id);
if (!instantiation) {
links.instantiations.set(id, instantiation = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters)))));
links.instantiations.set(id, instantiation = instantiateType(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters)))));
}
return instantiation;
}
@ -8034,11 +8070,6 @@ namespace ts {
return instantiateList(signatures, mapper, instantiateSignature);
}
function instantiateCached<T extends Type>(type: T, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T {
const instantiations = mapper.instantiations || (mapper.instantiations = []);
return <T>instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper));
}
function makeUnaryTypeMapper(source: Type, target: Type) {
return (t: Type) => t === source ? target : t;
}
@ -8060,11 +8091,9 @@ namespace ts {
function createTypeMapper(sources: TypeParameter[], targets: Type[]): TypeMapper {
Debug.assert(targets === undefined || sources.length === targets.length);
const mapper: TypeMapper = sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
return sources.length === 1 ? makeUnaryTypeMapper(sources[0], targets ? targets[0] : anyType) :
sources.length === 2 ? makeBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
makeArrayTypeMapper(sources, targets);
mapper.mappedTypes = sources;
return mapper;
makeArrayTypeMapper(sources, targets);
}
function createTypeEraser(sources: TypeParameter[]): TypeMapper {
@ -8075,10 +8104,8 @@ namespace ts {
* Maps forward-references to later types parameters to the empty object type.
* This is used during inference when instantiating type parameter defaults.
*/
function createBackreferenceMapper(typeParameters: TypeParameter[], index: number) {
const mapper: TypeMapper = t => indexOf(typeParameters, t) >= index ? emptyObjectType : t;
mapper.mappedTypes = typeParameters;
return mapper;
function createBackreferenceMapper(typeParameters: TypeParameter[], index: number): TypeMapper {
return t => indexOf(typeParameters, t) >= index ? emptyObjectType : t;
}
function isInferenceContext(mapper: TypeMapper): mapper is InferenceContext {
@ -8096,15 +8123,11 @@ namespace ts {
}
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
const mapper: TypeMapper = t => instantiateType(mapper1(t), mapper2);
mapper.mappedTypes = concatenate(mapper1.mappedTypes, mapper2.mappedTypes);
return mapper;
return t => instantiateType(mapper1(t), mapper2);
}
function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper) {
const mapper: TypeMapper = t => t === source ? target : baseMapper(t);
mapper.mappedTypes = baseMapper.mappedTypes;
return mapper;
function createReplacementMapper(source: Type, target: Type, baseMapper: TypeMapper): TypeMapper {
return t => t === source ? target : baseMapper(t);
}
function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter {
@ -8181,13 +8204,53 @@ namespace ts {
return result;
}
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
const result = <AnonymousType>createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol);
result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper);
return result;
function getAnonymousTypeInstantiation(type: AnonymousType, mapper: TypeMapper) {
const target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type;
const symbol = target.symbol;
const links = getSymbolLinks(symbol);
let typeParameters = links.typeParameters;
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
// aren't the right hand side of a generic type alias declaration we optimize by reducing the
// set of type parameters to those that are actually referenced somewhere in the literal.
const declaration = symbol.declarations[0];
const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray;
typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ?
filter(outerTypeParameters, tp => isTypeParameterReferencedWithin(tp, declaration)) :
outerTypeParameters;
links.typeParameters = typeParameters;
if (typeParameters.length) {
links.instantiations = createMap<Type>();
links.instantiations.set(getTypeListId(typeParameters), target);
}
}
if (typeParameters.length) {
// We are instantiating an anonymous type that has one or more type parameters in scope. Apply the
// mapper to the type parameters to produce the effective list of type arguments, and compute the
// instantiation cache key from the type IDs of the type arguments.
const combinedMapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper;
const typeArguments = map(typeParameters, combinedMapper);
const id = getTypeListId(typeArguments);
let result = links.instantiations.get(id);
if (!result) {
const newMapper = createTypeMapper(typeParameters, typeArguments);
result = target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(<MappedType>target, newMapper) : instantiateAnonymousType(target, newMapper);
links.instantiations.set(id, result);
}
return result;
}
return type;
}
function isTypeParameterReferencedWithin(tp: TypeParameter, node: Node) {
return tp.isThisType ? forEachChild(node, checkThis) : forEachChild(node, checkIdentifier);
function checkThis(node: Node): boolean {
return node.kind === SyntaxKind.ThisType || forEachChild(node, checkThis);
}
function checkIdentifier(node: Node): boolean {
return node.kind === SyntaxKind.Identifier && isPartOfTypeNode(node) && getTypeFromTypeNode(<TypeNode>node) === tp || forEachChild(node, checkIdentifier);
}
}
function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type {
@ -8204,164 +8267,64 @@ namespace ts {
if (typeVariable !== mappedTypeVariable) {
return mapType(mappedTypeVariable, t => {
if (isMappableType(t)) {
return instantiateMappedObjectType(type, createReplacementMapper(typeVariable, t, mapper));
return instantiateAnonymousType(type, createReplacementMapper(typeVariable, t, mapper));
}
return t;
});
}
}
}
return instantiateMappedObjectType(type, mapper);
return instantiateAnonymousType(type, mapper);
}
function isMappableType(type: Type) {
return type.flags & (TypeFlags.TypeParameter | TypeFlags.Object | TypeFlags.Intersection | TypeFlags.IndexedAccess);
}
function instantiateMappedObjectType(type: MappedType, mapper: TypeMapper): Type {
const result = <MappedType>createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol);
result.declaration = type.declaration;
result.mapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType {
const result = <AnonymousType>createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol);
if (type.objectFlags & ObjectFlags.Mapped) {
(<MappedType>result).declaration = (<MappedType>type).declaration;
}
result.target = type;
result.mapper = mapper;
result.aliasSymbol = type.aliasSymbol;
result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper);
return result;
}
function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) {
if (!(symbol.declarations && symbol.declarations.length)) {
return false;
}
const mappedTypes = mapper.mappedTypes;
// Starting with the parent of the symbol's declaration, check if the mapper maps any of
// the type parameters introduced by enclosing declarations. We just pick the first
// declaration since multiple declarations will all have the same parent anyway.
return !!findAncestor(symbol.declarations[0], node => {
if (node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) {
return "quit";
}
switch (node.kind) {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
const typeParameters = getEffectiveTypeParameterDeclarations(node as DeclarationWithTypeParameters);
if (typeParameters) {
for (const d of typeParameters) {
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode(d)))) {
return true;
}
}
}
if (isClassLike(node) || node.kind === SyntaxKind.InterfaceDeclaration) {
const thisType = getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node)).thisType;
if (thisType && contains(mappedTypes, thisType)) {
return true;
}
}
break;
case SyntaxKind.MappedType:
if (contains(mappedTypes, getDeclaredTypeOfTypeParameter(getSymbolOfNode((<MappedTypeNode>node).typeParameter)))) {
return true;
}
break;
case SyntaxKind.JSDocFunctionType:
const func = node as JSDocFunctionType;
for (const p of func.parameters) {
if (contains(mappedTypes, getTypeOfNode(p))) {
return true;
}
}
break;
}
});
}
function isTopLevelTypeAlias(symbol: Symbol) {
if (symbol.declarations && symbol.declarations.length) {
const parentKind = symbol.declarations[0].parent.kind;
return parentKind === SyntaxKind.SourceFile || parentKind === SyntaxKind.ModuleBlock;
}
return false;
}
function instantiateType(type: Type, mapper: TypeMapper): Type {
if (type && mapper !== identityMapper) {
// If we are instantiating a type that has a top-level type alias, obtain the instantiation through
// the type alias instead in order to share instantiations for the same type arguments. This can
// dramatically reduce the number of structurally identical types we generate. Note that we can only
// perform this optimization for top-level type aliases. Consider:
//
// function f1<T>(x: T) {
// type Foo<X> = { x: X, t: T };
// let obj: Foo<T> = { x: x };
// return obj;
// }
// function f2<U>(x: U) { return f1(x); }
// let z = f2(42);
//
// Above, the declaration of f2 has an inferred return type that is an instantiation of f1's Foo<X>
// equivalent to { x: U, t: U }. When instantiating this return type, we can't go back to Foo<X>'s
// cache because all cached instantiations are of the form { x: ???, t: T }, i.e. they have not been
// instantiated for T. Instead, we need to further instantiate the { x: U, t: U } form.
if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) {
if (type.aliasTypeArguments) {
return getTypeAliasInstantiation(type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
if (type.flags & TypeFlags.TypeParameter) {
return mapper(<TypeParameter>type);
}
if (type.flags & TypeFlags.Object) {
if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
// If the anonymous type originates in a declaration of a function, method, class, or
// interface, in an object type literal, or in an object literal expression, we may need
// to instantiate the type because it might reference a type parameter.
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ?
getAnonymousTypeInstantiation(<AnonymousType>type, mapper) : type;
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
return getAnonymousTypeInstantiation(<MappedType>type, mapper);
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateTypes((<TypeReference>type).typeArguments, mapper));
}
return type;
}
return instantiateTypeNoAlias(type, mapper);
}
return type;
}
function instantiateTypeNoAlias(type: Type, mapper: TypeMapper): Type {
if (type.flags & TypeFlags.TypeParameter) {
return mapper(<TypeParameter>type);
}
if (type.flags & TypeFlags.Object) {
if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
// If the anonymous type originates in a declaration of a function, method, class, or
// interface, in an object type literal, or in an object literal expression, we may need
// to instantiate the type because it might reference a type parameter. We skip instantiation
// if none of the type parameters that are in scope in the type's declaration are mapped by
// the given mapper, however we can only do that analysis if the type isn't itself an
// instantiation.
return type.symbol &&
type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) &&
((<ObjectType>type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ?
instantiateCached(type, mapper, instantiateAnonymousType) : type;
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
return getUnionType(instantiateTypes((<UnionType>type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
return instantiateCached(type, mapper, instantiateMappedType);
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateTypes((<IntersectionType>type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
return createTypeReference((<TypeReference>type).target, instantiateTypes((<TypeReference>type).typeArguments, mapper));
if (type.flags & TypeFlags.Index) {
return getIndexType(instantiateType((<IndexType>type).type, mapper));
}
if (type.flags & TypeFlags.IndexedAccess) {
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
}
}
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
return getUnionType(instantiateTypes((<UnionType>type).types, mapper), /*subtypeReduction*/ false, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(instantiateTypes((<IntersectionType>type).types, mapper), type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
}
if (type.flags & TypeFlags.Index) {
return getIndexType(instantiateType((<IndexType>type).type, mapper));
}
if (type.flags & TypeFlags.IndexedAccess) {
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
}
return type;
}
@ -8538,7 +8501,8 @@ namespace ts {
return Ternary.False;
}
if (source.typeParameters) {
if (source.typeParameters && source.typeParameters !== target.typeParameters) {
target = getCanonicalSignature(target);
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
}
@ -10356,7 +10320,6 @@ namespace ts {
function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
const context = mapper as InferenceContext;
context.mappedTypes = signature.typeParameters;
context.signature = signature;
context.inferences = inferences;
context.flags = flags;

View File

@ -438,8 +438,10 @@ namespace ts {
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
}
case SyntaxKind.JSDocTypeLiteral:
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
visitNode(cbNode, tag);
if ((node as JSDocTypeLiteral).jsDocPropertyTags) {
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
visitNode(cbNode, tag);
}
}
return;
case SyntaxKind.PartiallyEmittedExpression:
@ -6667,19 +6669,18 @@ namespace ts {
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
let child: JSDocTypeTag | JSDocPropertyTag | false;
let jsdocTypeLiteral: JSDocTypeLiteral;
let alreadyHasTypeTag = false;
let childTypeTag: JSDocTypeTag;
const start = scanner.getStartPos();
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) {
if (!jsdocTypeLiteral) {
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
}
if (child.kind === SyntaxKind.JSDocTypeTag) {
if (alreadyHasTypeTag) {
if (childTypeTag) {
break;
}
else {
jsdocTypeLiteral.jsDocTypeTag = child;
alreadyHasTypeTag = true;
childTypeTag = child;
}
}
else {
@ -6693,7 +6694,9 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
typedefTag.typeExpression = finishNode(jsdocTypeLiteral);
typedefTag.typeExpression = childTypeTag && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
childTypeTag.typeExpression :
finishNode(jsdocTypeLiteral);
}
}

View File

@ -2150,7 +2150,6 @@ namespace ts {
export interface JSDocTypeLiteral extends JSDocType {
kind: SyntaxKind.JSDocTypeLiteral;
jsDocPropertyTags?: ReadonlyArray<JSDocPropertyLikeTag>;
jsDocTypeTag?: JSDocTypeTag;
/** If true, then this type literal represents an *array* of its type. */
isArrayType?: boolean;
}
@ -3296,13 +3295,12 @@ namespace ts {
}
/* @internal */
export interface MappedType extends ObjectType {
export interface MappedType extends AnonymousType {
declaration: MappedTypeNode;
typeParameter?: TypeParameter;
constraintType?: Type;
templateType?: Type;
modifiersType?: Type;
mapper?: TypeMapper; // Instantiation mapper
}
export interface EvolvingArrayType extends ObjectType {
@ -3412,6 +3410,8 @@ namespace ts {
/* @internal */
erasedSignatureCache?: Signature; // Erased version of signature (deferred)
/* @internal */
canonicalSignatureCache?: Signature; // Canonical version of signature (deferred)
/* @internal */
isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison
/* @internal */
typePredicate?: TypePredicate;
@ -3433,8 +3433,6 @@ namespace ts {
/* @internal */
export interface TypeMapper {
(t: TypeParameter): Type;
mappedTypes?: TypeParameter[]; // Types mapped by this mapper
instantiations?: Type[]; // Cache of instantiations created using this type mapper.
}
export const enum InferencePriority {

View File

@ -235,24 +235,39 @@ namespace ts.server {
return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`;
}
interface QueuedOperation {
operationId: string;
operation: () => void;
}
class NodeTypingsInstaller implements ITypingsInstaller {
private installer: NodeChildProcess;
private installerPidReported = false;
private socket: NodeSocket;
private projectService: ProjectService;
private throttledOperations: ThrottledOperations;
private eventSender: EventSender;
private activeRequestCount = 0;
private requestQueue: QueuedOperation[] = [];
private requestMap = createMap<QueuedOperation>(); // Maps operation ID to newest requestQueue entry with that ID
// This number is essentially arbitrary. Processing more than one typings request
// at a time makes sense, but having too many in the pipe results in a hang
// (see https://github.com/nodejs/node/issues/7657).
// It would be preferable to base our limit on the amount of space left in the
// buffer, but we have yet to find a way to retrieve that value.
private static readonly maxActiveRequestCount = 10;
private static readonly requestDelayMillis = 100;
constructor(
private readonly telemetryEnabled: boolean,
private readonly logger: server.Logger,
host: ServerHost,
private readonly host: ServerHost,
eventPort: number,
readonly globalTypingsCacheLocation: string,
readonly typingSafeListLocation: string,
private readonly npmLocation: string | undefined,
private newLine: string) {
this.throttledOperations = new ThrottledOperations(host);
if (eventPort) {
const s = net.connect({ port: eventPort }, () => {
this.socket = s;
@ -333,12 +348,26 @@ namespace ts.server {
this.logger.info(`Scheduling throttled operation: ${JSON.stringify(request)}`);
}
}
this.throttledOperations.schedule(project.getProjectName(), /*ms*/ 250, () => {
const operationId = project.getProjectName();
const operation = () => {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Sending request: ${JSON.stringify(request)}`);
}
this.installer.send(request);
});
};
const queuedRequest: QueuedOperation = { operationId, operation };
if (this.activeRequestCount < NodeTypingsInstaller.maxActiveRequestCount) {
this.scheduleRequest(queuedRequest);
}
else {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Deferring request for: ${operationId}`);
}
this.requestQueue.push(queuedRequest);
this.requestMap.set(operationId, queuedRequest);
}
}
private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
@ -399,11 +428,39 @@ namespace ts.server {
return;
}
if (this.activeRequestCount > 0) {
this.activeRequestCount--;
}
else {
Debug.fail("Received too many responses");
}
while (this.requestQueue.length > 0) {
const queuedRequest = this.requestQueue.shift();
if (this.requestMap.get(queuedRequest.operationId) === queuedRequest) {
this.requestMap.delete(queuedRequest.operationId);
this.scheduleRequest(queuedRequest);
break;
}
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Skipping defunct request for: ${queuedRequest.operationId}`);
}
}
this.projectService.updateTypingsForProject(response);
if (response.kind === ActionSet && this.socket) {
this.sendEvent(0, "setTypings", response);
}
}
private scheduleRequest(request: QueuedOperation) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Scheduling request for: ${request.operationId}`);
}
this.activeRequestCount++;
this.host.setTimeout(request.operation, NodeTypingsInstaller.requestDelayMillis);
}
}
class IOSession extends Session {

View File

@ -179,6 +179,12 @@ namespace ts.server {
constructor(private readonly host: ServerHost) {
}
/**
* Wait `number` milliseconds and then invoke `cb`. If, while waiting, schedule
* is called again with the same `operationId`, cancel this operation in favor
* of the new one. (Note that the amount of time the canceled operation had been
* waiting does not affect the amount of time that the new operation waits.)
*/
public schedule(operationId: string, delay: number, cb: () => void) {
const pendingTimeout = this.pendingTimeouts.get(operationId);
if (pendingTimeout) {

View File

@ -372,8 +372,7 @@ namespace ts.SignatureHelp {
if (isTypeParameterList) {
isVariadic = false; // type parameter lists are not variadic
prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken));
// Use `.mapper` to ensure we get the generic type arguments even if this is an instantiated version of the signature.
const typeParameters = candidateSignature.mapper ? candidateSignature.mapper.mappedTypes : candidateSignature.typeParameters;
const typeParameters = (candidateSignature.target || candidateSignature).typeParameters;
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
const parameterParts = mapToDisplayParts(writer =>

View File

@ -34,38 +34,6 @@
"kind": "JSDocTypeLiteral",
"pos": 26,
"end": 98,
"jsDocTypeTag": {
"kind": "JSDocTypeTag",
"pos": 28,
"end": 42,
"atToken": {
"kind": "AtToken",
"pos": 28,
"end": 29
},
"tagName": {
"kind": "Identifier",
"pos": 29,
"end": 33,
"escapedText": "type"
},
"typeExpression": {
"kind": "JSDocTypeExpression",
"pos": 34,
"end": 42,
"type": {
"kind": "TypeReference",
"pos": 35,
"end": 41,
"typeName": {
"kind": "Identifier",
"pos": 35,
"end": 41,
"escapedText": "Object"
}
}
}
},
"jsDocPropertyTags": [
{
"kind": "JSDocPropertyTag",

View File

@ -22,8 +22,6 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstrain
Type 'void' is not assignable to type 'string'.
tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts(38,10): error TS2345: Argument of type 'U' is not assignable to parameter of type '(x: string) => string'.
Type 'T' is not assignable to type '(x: string) => string'.
Type '() => void' is not assignable to type '(x: string) => string'.
Type 'void' is not assignable to type 'string'.
==== tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstraintSatisfaction2.ts (13 errors) ====
@ -102,7 +100,5 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/functionConstrain
~
!!! error TS2345: Argument of type 'U' is not assignable to parameter of type '(x: string) => string'.
!!! error TS2345: Type 'T' is not assignable to type '(x: string) => string'.
!!! error TS2345: Type '() => void' is not assignable to type '(x: string) => string'.
!!! error TS2345: Type 'void' is not assignable to type 'string'.
}

View File

@ -0,0 +1,10 @@
//// [jsdocTwoLineTypedef.ts]
// Regression from #18301
/**
* @typedef LoadCallback
* @type {function}
*/
type LoadCallback = void;
//// [jsdocTwoLineTypedef.js]

View File

@ -0,0 +1,9 @@
=== tests/cases/conformance/jsdoc/jsdocTwoLineTypedef.ts ===
// Regression from #18301
/**
* @typedef LoadCallback
* @type {function}
*/
type LoadCallback = void;
>LoadCallback : Symbol(LoadCallback, Decl(jsdocTwoLineTypedef.ts, 0, 0))

View File

@ -0,0 +1,9 @@
=== tests/cases/conformance/jsdoc/jsdocTwoLineTypedef.ts ===
// Regression from #18301
/**
* @typedef LoadCallback
* @type {function}
*/
type LoadCallback = void;
>LoadCallback : void

View File

@ -1,4 +1,4 @@
tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2550: Generic type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2502: '"true"' is referenced directly or indirectly in its own type annotation.
tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'.
@ -7,7 +7,7 @@ tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"fals
type Foo<T extends "true", B> = { "true": Foo<T, Foo<T, B>> }[T];
~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2550: Generic type instantiation is excessively deep and possibly infinite.
!!! error TS2502: '"true"' is referenced directly or indirectly in its own type annotation.
let f1: Foo<"true", {}>;
let f2: Foo<"false", {}>;
~~~~~~~

View File

@ -45,7 +45,6 @@ tests/cases/compiler/promisePermutations.ts(134,19): error TS2345: Argument of t
tests/cases/compiler/promisePermutations.ts(137,33): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
tests/cases/compiler/promisePermutations.ts(144,35): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
tests/cases/compiler/promisePermutations.ts(152,36): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
Types of property 'then' are incompatible.
@ -290,7 +289,6 @@ tests/cases/compiler/promisePermutations.ts(160,21): error TS2345: Argument of t
var r10d = r10.then(testFunction, sIPromise, nIPromise); // ok
~~~~~~~~~
!!! error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
!!! error TS2345: Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
var r10e = r10.then(testFunction, nIPromise, sIPromise).then(sIPromise, sIPromise, sIPromise); // ok
var s10 = testFunction10P(x => x);
var s10a = s10.then(testFunction10, testFunction10, testFunction10); // ok

View File

@ -45,7 +45,6 @@ tests/cases/compiler/promisePermutations2.ts(133,19): error TS2345: Argument of
tests/cases/compiler/promisePermutations2.ts(136,33): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
tests/cases/compiler/promisePermutations2.ts(143,35): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
tests/cases/compiler/promisePermutations2.ts(151,36): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
Types of property 'then' are incompatible.
@ -289,7 +288,6 @@ tests/cases/compiler/promisePermutations2.ts(159,21): error TS2345: Argument of
var r10d = r10.then(testFunction, sIPromise, nIPromise); // error
~~~~~~~~~
!!! error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
!!! error TS2345: Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
var r10e = r10.then(testFunction, nIPromise, sIPromise).then(sIPromise, sIPromise, sIPromise); // ok
var s10 = testFunction10P(x => x);
var s10a = s10.then(testFunction10, testFunction10, testFunction10); // ok

View File

@ -48,7 +48,6 @@ tests/cases/compiler/promisePermutations3.ts(133,19): error TS2345: Argument of
tests/cases/compiler/promisePermutations3.ts(136,33): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
tests/cases/compiler/promisePermutations3.ts(143,35): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
tests/cases/compiler/promisePermutations3.ts(151,36): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
Types of property 'then' are incompatible.
@ -301,7 +300,6 @@ tests/cases/compiler/promisePermutations3.ts(165,21): error TS2345: Argument of
var r10d = r10.then(testFunction, sIPromise, nIPromise); // error
~~~~~~~~~
!!! error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => IPromise<number>'.
!!! error TS2345: Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
var r10e = r10.then(testFunction, nIPromise, sIPromise).then(sIPromise, sIPromise, sIPromise); // ok
var s10 = testFunction10P(x => x);
var s10a = s10.then(testFunction10, testFunction10, testFunction10); // ok

View File

@ -0,0 +1,6 @@
// Regression from #18301
/**
* @typedef LoadCallback
* @type {function}
*/
type LoadCallback = void;