From 6769313eee2f2403387ea935a4fef4dfddae7671 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 28 Jan 2020 16:48:28 -0800 Subject: [PATCH] Fix potential leaks in checker (#36491) --- scripts/configurePrerelease.ts | 2 +- src/compiler/checker.ts | 151 +++++++++++++++++------------ src/compiler/moduleNameResolver.ts | 2 - src/compiler/types.ts | 5 +- 4 files changed, 96 insertions(+), 64 deletions(-) diff --git a/scripts/configurePrerelease.ts b/scripts/configurePrerelease.ts index 94fdefb361e..4eee5d2b34e 100644 --- a/scripts/configurePrerelease.ts +++ b/scripts/configurePrerelease.ts @@ -60,7 +60,7 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st const parsedMajorMinor = majorMinorMatch![1]; assert(parsedMajorMinor === majorMinor, `versionMajorMinor does not match. ${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`); - const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)(-dev)?`;/; + const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)(-\w+)?`;/; const patchMatch = versionRgx.exec(tsFileContents); assert(patchMatch !== null, `The file '${tsFilePath}' seems to no longer have a string matching '${versionRgx.toString()}'.`); const parsedPatch = patchMatch![1]; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index efbe0596b70..c350a257c0c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -738,10 +738,11 @@ namespace ts { const iterationTypesCache = createMap(); // cache for common IterationTypes instances const noIterationTypes: IterationTypes = { - get yieldType(): Type { throw new Error("Not supported"); }, - get returnType(): Type { throw new Error("Not supported"); }, - get nextType(): Type { throw new Error("Not supported"); }, + get yieldType(): Type { return Debug.fail("Not supported"); }, + get returnType(): Type { return Debug.fail("Not supported"); }, + get nextType(): Type { return Debug.fail("Not supported"); }, }; + const anyIterationTypes = createIterationTypes(anyType, anyType, anyType); const anyIterationTypesExceptNext = createIterationTypes(anyType, anyType, unknownType); const defaultIterationTypes = createIterationTypes(neverType, anyType, undefinedType); // default iteration types for `Iterator`. @@ -2323,8 +2324,8 @@ namespace ts { result.declarations = deduplicate(concatenate(valueSymbol.declarations, typeSymbol.declarations), equateValues); result.parent = valueSymbol.parent || typeSymbol.parent; if (valueSymbol.valueDeclaration) result.valueDeclaration = valueSymbol.valueDeclaration; - if (typeSymbol.members) result.members = typeSymbol.members; - if (valueSymbol.exports) result.exports = valueSymbol.exports; + if (typeSymbol.members) result.members = cloneMap(typeSymbol.members); + if (valueSymbol.exports) result.exports = cloneMap(valueSymbol.exports); return result; } @@ -5095,7 +5096,7 @@ namespace ts { // See getNameForSymbolFromNameType for a stringy equivalent function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote?: boolean) { - const nameType = symbol.nameType; + const nameType = getSymbolLinks(symbol).nameType; if (nameType) { if (nameType.flags & TypeFlags.StringOrNumberLiteral) { const name = "" + (nameType).value; @@ -6521,7 +6522,7 @@ namespace ts { } function getNameOfSymbolFromNameType(symbol: Symbol, context?: NodeBuilderContext) { - const nameType = symbol.nameType; + const nameType = getSymbolLinks(symbol).nameType; if (nameType) { if (nameType.flags & TypeFlags.StringOrNumberLiteral) { const name = "" + (nameType).value; @@ -6563,11 +6564,14 @@ namespace ts { if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) { return symbolName(symbol); } - if (isComputedPropertyName(name) && !(getCheckFlags(symbol) & CheckFlags.Late) && symbol.nameType && symbol.nameType.flags & TypeFlags.StringOrNumberLiteral) { - // Computed property name isn't late bound, but has a well-known name type - use name type to generate a symbol name - const result = getNameOfSymbolFromNameType(symbol, context); - if (result !== undefined) { - return result; + if (isComputedPropertyName(name) && !(getCheckFlags(symbol) & CheckFlags.Late)) { + const nameType = getSymbolLinks(symbol).nameType; + if (nameType && nameType.flags & TypeFlags.StringOrNumberLiteral) { + // Computed property name isn't late bound, but has a well-known name type - use name type to generate a symbol name + const result = getNameOfSymbolFromNameType(symbol, context); + if (result !== undefined) { + return result; + } } } return declarationNameToString(name); @@ -8697,7 +8701,7 @@ namespace ts { * @param lateSymbols The late-bound symbols of the parent. * @param decl The member to bind. */ - function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: SymbolTable, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration) { + function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: UnderscoreEscapedMap, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration) { Debug.assert(!!decl.symbol, "The member is expected to have a symbol."); const links = getNodeLinks(decl); if (!links.resolvedSymbol) { @@ -8755,7 +8759,7 @@ namespace ts { links[resolutionKind] = earlySymbols || emptySymbols; // fill in any as-yet-unresolved late-bound members. - const lateSymbols = createSymbolTable(); + const lateSymbols = createSymbolTable() as UnderscoreEscapedMap; for (const decl of symbol.declarations) { const members = getMembersOfDeclaration(decl); if (members) { @@ -9359,7 +9363,7 @@ namespace ts { const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; inferredProp.declarations = prop.declarations; - inferredProp.nameType = prop.nameType; + inferredProp.nameType = getSymbolLinks(prop).nameType; inferredProp.propertyType = getTypeOfSymbol(prop); inferredProp.mappedType = type.mappedType; inferredProp.constraintType = type.constraintType; @@ -10048,7 +10052,7 @@ namespace ts { const type = getTypeOfSymbol(prop); if (!firstType) { firstType = type; - nameType = prop.nameType; + nameType = getSymbolLinks(prop).nameType; } else if (type !== firstType) { checkFlags |= CheckFlags.HasNonUniformType; @@ -12040,7 +12044,7 @@ namespace ts { function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags) { if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) { - let type = getLateBoundSymbol(prop).nameType; + let type = getSymbolLinks(getLateBoundSymbol(prop)).nameType; if (!type && !isKnownSymbol(prop)) { if (prop.escapedName === InternalSymbolName.Default) { type = getLiteralType("default"); @@ -12812,7 +12816,7 @@ namespace ts { const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0); result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); result.declarations = prop.declarations; - result.nameType = prop.nameType; + result.nameType = getSymbolLinks(prop).nameType; result.syntheticOrigin = prop; members.set(prop.escapedName, result); } @@ -12920,7 +12924,7 @@ namespace ts { result.leftSpread = leftProp; result.rightSpread = rightProp; result.declarations = declarations; - result.nameType = leftProp.nameType; + result.nameType = getSymbolLinks(leftProp).nameType; members.set(leftProp.escapedName, result); } } @@ -12956,7 +12960,7 @@ namespace ts { const result = createSymbol(flags, prop.escapedName, readonly ? CheckFlags.Readonly : 0); result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); result.declarations = prop.declarations; - result.nameType = prop.nameType; + result.nameType = getSymbolLinks(prop).nameType; result.syntheticOrigin = prop; return result; } @@ -13322,8 +13326,8 @@ namespace ts { if (symbol.valueDeclaration) { result.valueDeclaration = symbol.valueDeclaration; } - if (symbol.nameType) { - result.nameType = symbol.nameType; + if (links.nameType) { + result.nameType = links.nameType; } return result; } @@ -16280,7 +16284,11 @@ namespace ts { const props = source.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(source) : getPropertiesOfObjectType(source); for (const prop of props) { // Skip over ignored JSX and symbol-named members - if (isIgnoredJsxProperty(source, prop) || prop.nameType && prop.nameType.flags & TypeFlags.UniqueESSymbol) { + if (isIgnoredJsxProperty(source, prop)) { + continue; + } + const nameType = getSymbolLinks(prop).nameType; + if (nameType && nameType.flags & TypeFlags.UniqueESSymbol) { continue; } if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) { @@ -17111,8 +17119,9 @@ namespace ts { if (source.valueDeclaration) { symbol.valueDeclaration = source.valueDeclaration; } - if (source.nameType) { - symbol.nameType = source.nameType; + const nameType = getSymbolLinks(source).nameType; + if (nameType) { + symbol.nameType = nameType; } return symbol; } @@ -27735,17 +27744,24 @@ namespace ts { const context = getContextNode(node); const saveContextualType = context.contextualType; const saveInferenceContext = context.inferenceContext; - context.contextualType = contextualType; - context.inferenceContext = inferenceContext; - const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0)); - // We strip literal freshness when an appropriate contextual type is present such that contextually typed - // literals always preserve their literal types (otherwise they might widen during type inference). An alternative - // here would be to not mark contextually typed literals as fresh in the first place. - const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ? - getRegularTypeOfLiteralType(type) : type; - context.contextualType = saveContextualType; - context.inferenceContext = saveInferenceContext; - return result; + try { + context.contextualType = contextualType; + context.inferenceContext = inferenceContext; + const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0)); + // We strip literal freshness when an appropriate contextual type is present such that contextually typed + // literals always preserve their literal types (otherwise they might widen during type inference). An alternative + // here would be to not mark contextually typed literals as fresh in the first place. + const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node)) ? + getRegularTypeOfLiteralType(type) : type; + return result; + } + finally { + // In the event our operation is canceled or some other exception occurs, reset the contextual type + // so that we do not accidentally hold onto an instance of the checker, as a Type created in the services layer + // may hold onto the checker that created it. + context.contextualType = saveContextualType; + context.inferenceContext = saveInferenceContext; + } } function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type { @@ -28090,9 +28106,16 @@ namespace ts { } const saveContextualType = node.contextualType; node.contextualType = anyType; - const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive); - node.contextualType = saveContextualType; - return type; + try { + const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive); + return type; + } + finally { + // In the event our operation is canceled or some other exception occurs, reset the contextual type + // so that we do not accidentally hold onto an instance of the checker, as a Type created in the services layer + // may hold onto the checker that created it. + node.contextualType = saveContextualType; + } } function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { @@ -31241,6 +31264,14 @@ namespace ts { return noIterationTypes; } + function getCachedIterationTypes(type: Type, cacheKey: MatchingKeys) { + return (type as IterableOrIteratorType)[cacheKey]; + } + + function setCachedIterationTypes(type: Type, cacheKey: MatchingKeys, cachedTypes: IterationTypes) { + return (type as IterableOrIteratorType)[cacheKey] = cachedTypes; + } + /** * Gets the *yield*, *return*, and *next* types from an `Iterable`-like or `AsyncIterable`-like type. * @@ -31279,7 +31310,7 @@ namespace ts { } const cacheKey = use & IterationUse.AllowsAsyncIterablesFlag ? "iterationTypesOfAsyncIterable" : "iterationTypesOfIterable"; - const cachedTypes = (type as IterableOrIteratorType)[cacheKey]; + const cachedTypes = getCachedIterationTypes(type, cacheKey); if (cachedTypes) return cachedTypes === noIterationTypes ? undefined : cachedTypes; let allIterationTypes: IterationTypes[] | undefined; @@ -31297,7 +31328,7 @@ namespace ts { } const iterationTypes = allIterationTypes ? combineIterationTypes(allIterationTypes) : noIterationTypes; - (type as IterableOrIteratorType)[cacheKey] = iterationTypes; + setCachedIterationTypes(type, cacheKey, iterationTypes); return iterationTypes === noIterationTypes ? undefined : iterationTypes; } @@ -31343,7 +31374,7 @@ namespace ts { if (use & IterationUse.AllowsAsyncIterablesFlag) { // for a sync iterable in an async context, only use the cached types if they are valid. if (iterationTypes !== noIterationTypes) { - return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = getAsyncFromSyncIterationTypes(iterationTypes, errorNode); + return setCachedIterationTypes(type, "iterationTypesOfAsyncIterable", getAsyncFromSyncIterationTypes(iterationTypes, errorNode)); } } else { @@ -31363,9 +31394,9 @@ namespace ts { const iterationTypes = getIterationTypesOfIterableSlow(type, syncIterationTypesResolver, errorNode); if (iterationTypes !== noIterationTypes) { if (use & IterationUse.AllowsAsyncIterablesFlag) { - return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = iterationTypes + return setCachedIterationTypes(type, "iterationTypesOfAsyncIterable", iterationTypes ? getAsyncFromSyncIterationTypes(iterationTypes, errorNode) - : noIterationTypes; + : noIterationTypes); } else { return iterationTypes; @@ -31384,7 +31415,7 @@ namespace ts { * `getIterationTypesOfIterable` instead. */ function getIterationTypesOfIterableCached(type: Type, resolver: IterationTypesResolver) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey]; + return getCachedIterationTypes(type, resolver.iterableCacheKey); } function getIterationTypesOfGlobalIterableType(globalType: Type, resolver: IterationTypesResolver) { @@ -31420,7 +31451,7 @@ namespace ts { // While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use // different definitions. const { returnType, nextType } = getIterationTypesOfGlobalIterableType(globalType, resolver); - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType); + return setCachedIterationTypes(type, resolver.iterableCacheKey, createIterationTypes(yieldType, returnType, nextType)); } // As an optimization, if the type is an instantiation of the following global type, then @@ -31428,7 +31459,7 @@ namespace ts { // - `Generator` or `AsyncGenerator` if (isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) { const [yieldType, returnType, nextType] = getTypeArguments(type as GenericType); - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType); + return setCachedIterationTypes(type, resolver.iterableCacheKey, createIterationTypes(yieldType, returnType, nextType)); } } @@ -31446,17 +31477,17 @@ namespace ts { const method = getPropertyOfType(type, getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName)); const methodType = method && !(method.flags & SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined; if (isTypeAny(methodType)) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = anyIterationTypes; + return setCachedIterationTypes(type, resolver.iterableCacheKey, anyIterationTypes); } const signatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined; if (!some(signatures)) { - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = noIterationTypes; + return setCachedIterationTypes(type, resolver.iterableCacheKey, noIterationTypes); } const iteratorType = getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype); - const iterationTypes = getIterationTypesOfIterator(iteratorType, resolver, errorNode) || noIterationTypes; - return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = iterationTypes; + const iterationTypes = getIterationTypesOfIterator(iteratorType, resolver, errorNode) ?? noIterationTypes; + return setCachedIterationTypes(type, resolver.iterableCacheKey, iterationTypes); } function reportTypeNotIterableError(errorNode: Node, type: Type, allowAsyncIterables: boolean): void { @@ -31492,7 +31523,7 @@ namespace ts { * `getIterationTypesOfIterator` instead. */ function getIterationTypesOfIteratorCached(type: Type, resolver: IterationTypesResolver) { - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey]; + return getCachedIterationTypes(type, resolver.iteratorCacheKey); } /** @@ -31523,12 +31554,12 @@ namespace ts { getIterationTypesOfIteratorCached(globalType, resolver) || getIterationTypesOfIteratorSlow(globalType, resolver, /*errorNode*/ undefined); const { returnType, nextType } = globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes; - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = createIterationTypes(yieldType, returnType, nextType); + return setCachedIterationTypes(type, resolver.iteratorCacheKey, createIterationTypes(yieldType, returnType, nextType)); } if (isReferenceToType(type, resolver.getGlobalIteratorType(/*reportErrors*/ false)) || isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) { const [yieldType, returnType, nextType] = getTypeArguments(type as GenericType); - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = createIterationTypes(yieldType, returnType, nextType); + return setCachedIterationTypes(type, resolver.iteratorCacheKey, createIterationTypes(yieldType, returnType, nextType)); } } @@ -31561,7 +31592,7 @@ namespace ts { return anyIterationTypes; } - const cachedTypes = (type as IterableOrIteratorType).iterationTypesOfIteratorResult; + const cachedTypes = getCachedIterationTypes(type, "iterationTypesOfIteratorResult"); if (cachedTypes) { return cachedTypes; } @@ -31570,11 +31601,11 @@ namespace ts { // or `IteratorReturnResult` types, then just grab its type argument. if (isReferenceToType(type, getGlobalIteratorYieldResultType(/*reportErrors*/ false))) { const yieldType = getTypeArguments(type as GenericType)[0]; - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(yieldType, /*returnType*/ undefined, /*nextType*/ undefined); + return setCachedIterationTypes(type, "iterationTypesOfIteratorResult", createIterationTypes(yieldType, /*returnType*/ undefined, /*nextType*/ undefined)); } if (isReferenceToType(type, getGlobalIteratorReturnResultType(/*reportErrors*/ false))) { const returnType = getTypeArguments(type as GenericType)[0]; - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(/*yieldType*/ undefined, returnType, /*nextType*/ undefined); + return setCachedIterationTypes(type, "iterationTypesOfIteratorResult", createIterationTypes(/*yieldType*/ undefined, returnType, /*nextType*/ undefined)); } // Choose any constituents that can produce the requested iteration type. @@ -31585,14 +31616,14 @@ namespace ts { const returnType = returnIteratorResult !== neverType ? getTypeOfPropertyOfType(returnIteratorResult, "value" as __String) : undefined; if (!yieldType && !returnType) { - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = noIterationTypes; + return setCachedIterationTypes(type, "iterationTypesOfIteratorResult", noIterationTypes); } // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface // > ... If the iterator does not have a return value, `value` is `undefined`. In that case, the // > `value` property may be absent from the conforming object if it does not inherit an explicit // > `value` property. - return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(yieldType, returnType || voidType, /*nextType*/ undefined); + return setCachedIterationTypes(type, "iterationTypesOfIteratorResult", createIterationTypes(yieldType, returnType || voidType, /*nextType*/ undefined)); } /** @@ -31694,7 +31725,7 @@ namespace ts { getIterationTypesOfMethod(type, resolver, "return", errorNode), getIterationTypesOfMethod(type, resolver, "throw", errorNode), ]); - return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = iterationTypes; + return setCachedIterationTypes(type, resolver.iteratorCacheKey, iterationTypes); } /** diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index aad8f9acd87..28550ab8cd1 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -112,8 +112,6 @@ namespace ts { version?: string; } - type MatchingKeys = K extends (TRecord[K] extends TMatch ? K : never) ? K : never; - function readPackageJsonField>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string", state: ModuleResolutionState): PackageJson[K] | undefined; function readPackageJsonField>(jsonContent: PackageJson, fieldName: K, typeOfTag: "object", state: ModuleResolutionState): PackageJson[K] | undefined; function readPackageJsonField(jsonContent: PackageJson, fieldName: K, typeOfTag: "string" | "object", state: ModuleResolutionState): PackageJson[K] | undefined { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6cac10abf72..b5760f7a88e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3,6 +3,9 @@ namespace ts { // arbitrary file name can be converted to Path via toPath function export type Path = string & { __pathBrand: any }; + /* @internal */ + export type MatchingKeys = K extends (TRecord[K] extends TMatch ? K : never) ? K : never; + export interface TextRange { pos: number; end: number; @@ -4045,7 +4048,6 @@ namespace ts { /* @internal */ mergeId?: number; // Merge id (used to look up merged symbol) /* @internal */ parent?: Symbol; // Parent symbol /* @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol - /* @internal */ nameType?: Type; // Type associated with a late-bound symbol /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter. /* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? @@ -4058,6 +4060,7 @@ namespace ts { immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. target?: Symbol; // Resolved (non-alias) target of an alias type?: Type; // Type of value symbol + nameType?: Type; // Type associated with a late-bound symbol uniqueESSymbolType?: Type; // UniqueESSymbol type for a symbol declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference