diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a5fe578757d04..5aeb419fe462f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3935,12 +3935,12 @@ namespace ts { return typeCopy; } - function forEachSymbolTableInScope(enclosingDeclaration: Node | undefined, callback: (symbolTable: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean) => T): T { + function forEachSymbolTableInScope(enclosingDeclaration: Node | undefined, callback: (symbolTable: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean, scopeNode?: Node) => T): T { let result: T; for (let location = enclosingDeclaration; location; location = location.parent) { // Locals of a source file are not in scope (because they get merged into the global symbol table) if (location.locals && !isGlobalSourceFile(location)) { - if (result = callback(location.locals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true)) { + if (result = callback(location.locals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true, location)) { return result; } } @@ -3955,7 +3955,7 @@ namespace ts { // `sym` may not have exports if this module declaration is backed by the symbol for a `const` that's being rewritten // into a namespace - in such cases, it's best to just let the namespace appear empty (the const members couldn't have referred // to one another anyway) - if (result = callback(sym?.exports || emptySymbols, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true)) { + if (result = callback(sym?.exports || emptySymbols, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true, location)) { return result; } break; @@ -3976,7 +3976,7 @@ namespace ts { (table || (table = createSymbolTable())).set(key, memberSymbol); } }); - if (table && (result = callback(table))) { + if (table && (result = callback(table, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ false, location))) { return result; } break; @@ -3995,13 +3995,23 @@ namespace ts { if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) { return undefined; } + const links = getSymbolLinks(symbol); + const cache = (links.accessibleChainCache ||= new Map()); + // Go from enclosingDeclaration to the first scope we check, so the cache is keyed off the scope and thus shared more + const firstRelevantLocation = forEachSymbolTableInScope(enclosingDeclaration, (_, __, ___, node) => node); + const key = `${useOnlyExternalAliasing ? 0 : 1}|${firstRelevantLocation && getNodeId(firstRelevantLocation)}|${meaning}`; + if (cache.has(key)) { + return cache.get(key); + } const id = getSymbolId(symbol); let visitedSymbolTables = visitedSymbolTablesMap.get(id); if (!visitedSymbolTables) { visitedSymbolTablesMap.set(id, visitedSymbolTables = []); } - return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable); + const result = forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable); + cache.set(key, result); + return result; /** * @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) @@ -4479,7 +4489,7 @@ namespace ts { enclosingDeclaration, flags: flags || NodeBuilderFlags.None, // If no full tracker is provided, fake up a dummy one with a basic limited-functionality moduleResolverHost - tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? { + tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: () => false, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? { getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "", getSourceFiles: () => host.getSourceFiles(), getCurrentDirectory: () => host.getCurrentDirectory(), @@ -4492,11 +4502,13 @@ namespace ts { getFileIncludeReasons: () => host.getFileIncludeReasons(), } : undefined }, encounteredError: false, + reportedDiagnostic: false, visitedTypes: undefined, symbolDepth: undefined, inferTypeParameters: undefined, approximateLength: 0 }; + context.tracker = wrapSymbolTrackerToReportForContext(context, context.tracker); const resultingNode = cb(context); if (context.truncating && context.flags & NodeBuilderFlags.NoTruncation) { context.tracker?.reportTruncationError?.(); @@ -4504,6 +4516,36 @@ namespace ts { return context.encounteredError ? undefined : resultingNode; } + function wrapSymbolTrackerToReportForContext(context: NodeBuilderContext, tracker: SymbolTracker): SymbolTracker { + const oldTrackSymbol = tracker.trackSymbol; + return { + ...tracker, + reportCyclicStructureError: wrapReportedDiagnostic(tracker.reportCyclicStructureError), + reportInaccessibleThisError: wrapReportedDiagnostic(tracker.reportInaccessibleThisError), + reportInaccessibleUniqueSymbolError: wrapReportedDiagnostic(tracker.reportInaccessibleUniqueSymbolError), + reportLikelyUnsafeImportRequiredError: wrapReportedDiagnostic(tracker.reportLikelyUnsafeImportRequiredError), + reportNonlocalAugmentation: wrapReportedDiagnostic(tracker.reportNonlocalAugmentation), + reportPrivateInBaseOfClassExpression: wrapReportedDiagnostic(tracker.reportPrivateInBaseOfClassExpression), + trackSymbol: oldTrackSymbol && ((...args) => { + const result = oldTrackSymbol(...args); + if (result) { + context.reportedDiagnostic = true; + } + return result; + }), + }; + + function wrapReportedDiagnostic any>(method: T | undefined): T | undefined { + if (!method) { + return method; + } + return (((...args) => { + context.reportedDiagnostic = true; + return method(...args); + }) as T); + } + } + function checkTruncationLength(context: NodeBuilderContext): boolean { if (context.truncating) return context.truncating; return context.truncating = context.approximateLength > ((context.flags & NodeBuilderFlags.NoTruncation) ? noTruncationMaximumTruncationLength : defaultMaximumTruncationLength); @@ -4830,7 +4872,7 @@ namespace ts { } } - function visitAndTransformType(type: Type, transform: (type: Type) => T) { + function visitAndTransformType(type: Type, transform: (type: Type) => T) { const typeId = type.id; const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class; const id = getObjectFlags(type) & ObjectFlags.Reference && (type).node ? "N" + getNodeId((type).node!) : @@ -4845,6 +4887,20 @@ namespace ts { context.symbolDepth = new Map(); } + const links = context.enclosingDeclaration && getNodeLinks(context.enclosingDeclaration); + const key = `${getTypeId(type)}|${context.flags}`; + if (links) { + links.serializedTypes ||= new Map(); + } + const cachedResult = links?.serializedTypes?.get(key); + if (cachedResult) { + if (cachedResult.truncating) { + context.truncating = true; + } + context.approximateLength += cachedResult.addedLength; + return deepCloneOrReuseNode(cachedResult) as TypeNode as T; + } + let depth: number | undefined; if (id) { depth = context.symbolDepth!.get(id) || 0; @@ -4854,12 +4910,28 @@ namespace ts { context.symbolDepth!.set(id, depth + 1); } context.visitedTypes.add(typeId); + const startLength = context.approximateLength; const result = transform(type); + const addedLength = context.approximateLength - startLength; + if (!context.reportedDiagnostic && !context.encounteredError) { + if (context.truncating) { + (result as any).truncating = true; + } + (result as any).addedLength = addedLength; + links?.serializedTypes?.set(key, result as TypeNode as TypeNode & {truncating?: boolean, addedLength: number}); + } context.visitedTypes.delete(typeId); if (id) { context.symbolDepth!.set(id, depth!); } return result; + + function deepCloneOrReuseNode(node: Node): Node { + if (!nodeIsSynthesized(node) && getParseTreeNode(node) === node) { + return node; + } + return setTextRange(factory.cloneNode(visitEachChild(node, deepCloneOrReuseNode, nullTransformationContext)), node); + } } function createTypeNodeFromObjectType(type: ObjectType): TypeNode { @@ -5346,6 +5418,7 @@ namespace ts { function signatureToSignatureDeclarationHelper(signature: Signature, kind: SignatureDeclaration["kind"], context: NodeBuilderContext, options?: SignatureToSignatureDeclarationOptions): SignatureDeclaration { const suppressAny = context.flags & NodeBuilderFlags.SuppressAnyReturnType; if (suppressAny) context.flags &= ~NodeBuilderFlags.SuppressAnyReturnType; // suppress only toplevel `any`s + context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum let typeParameters: TypeParameterDeclaration[] | undefined; let typeArguments: TypeNode[] | undefined; if (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature && signature.target && signature.mapper && signature.target.typeParameters) { @@ -5389,7 +5462,6 @@ namespace ts { const flags = modifiersToFlags(modifiers); modifiers = factory.createModifiersFromModifierFlags(flags | ModifierFlags.Abstract); } - context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum const node = kind === SyntaxKind.CallSignature ? factory.createCallSignature(typeParameters, parameters, returnTypeNode) : @@ -5816,7 +5888,7 @@ namespace ts { } if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { const rawtext = result.escapedText as string; - let i = 0; + let i = context.typeParameterNamesByTextNextNameCount?.get(rawtext) || 0; let text = rawtext; while (context.typeParameterNamesByText?.has(text) || typeParameterShadowsNameInScope(text as __String, context, type)) { i++; @@ -5825,8 +5897,11 @@ namespace ts { if (text !== rawtext) { result = factory.createIdentifier(text, result.typeArguments); } - (context.typeParameterNames || (context.typeParameterNames = new Map())).set(getTypeId(type), result); - (context.typeParameterNamesByText || (context.typeParameterNamesByText = new Set())).add(result.escapedText as string); + // avoiding iterations of the above loop turns out to be worth it when `i` starts to get large, so we cache the max + // `i` we've used thus far, to save work later + (context.typeParameterNamesByTextNextNameCount ||= new Map()).set(rawtext, i); + (context.typeParameterNames ||= new Map()).set(getTypeId(type), result); + (context.typeParameterNamesByText ||= new Set()).add(rawtext); } return result; } @@ -5987,6 +6062,7 @@ namespace ts { if (initial.typeParameterSymbolList) { initial.typeParameterSymbolList = new Set(initial.typeParameterSymbolList); } + initial.tracker = wrapSymbolTrackerToReportForContext(initial, initial.tracker); return initial; } @@ -6278,11 +6354,13 @@ namespace ts { } } else if (oldcontext.tracker && oldcontext.tracker.trackSymbol) { - oldcontext.tracker.trackSymbol(sym, decl, meaning); + return oldcontext.tracker.trackSymbol(sym, decl, meaning); } - } - } + return false; + }, + }, }; + context.tracker = wrapSymbolTrackerToReportForContext(context, context.tracker); forEachEntry(symbolTable, (symbol, name) => { const baseName = unescapeLeadingUnderscores(name); void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames` @@ -6502,6 +6580,9 @@ namespace ts { const oldContext = context; context = cloneNodeBuilderContext(context); const result = serializeSymbolWorker(symbol, isPrivate, propertyAsAlias); + if (context.reportedDiagnostic) { + oldcontext.reportedDiagnostic = context.reportedDiagnostic; // hoist diagnostic result into outer context + } context = oldContext; return result; } @@ -7275,7 +7356,7 @@ namespace ts { // a visibility error here (as they're not visible within any scope), but we want to hoist them // into the containing scope anyway, so we want to skip the visibility checks. const oldTrack = context.tracker.trackSymbol; - context.tracker.trackSymbol = noop; + context.tracker.trackSymbol = () => false; if (isExportAssignmentCompatibleSymbolName) { results.push(factory.createExportAssignment( /*decorators*/ undefined, @@ -7731,6 +7812,7 @@ namespace ts { // State encounteredError: boolean; + reportedDiagnostic: boolean; visitedTypes: Set | undefined; symbolDepth: ESMap | undefined; inferTypeParameters: TypeParameter[] | undefined; @@ -7739,6 +7821,7 @@ namespace ts { typeParameterSymbolList?: Set; typeParameterNames?: ESMap; typeParameterNamesByText?: Set; + typeParameterNamesByTextNextNameCount?: ESMap; usedSymbolNames?: Set; remappedSymbolNames?: ESMap; reverseMappedStack?: ReverseMappedSymbol[]; diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 5a56075706720..9732458c7a94d 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -145,8 +145,10 @@ namespace ts { symbolAccessibilityResult.errorSymbolName, symbolAccessibilityResult.errorModuleName)); } + return true; } } + return false; } function trackExternalModuleSymbolOfImportTypeNode(symbol: Symbol) { @@ -156,9 +158,10 @@ namespace ts { } function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) { - if (symbol.flags & SymbolFlags.TypeParameter) return; - handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true)); + if (symbol.flags & SymbolFlags.TypeParameter) return false; + const issuedDiagnostic = handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true)); recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning)); + return issuedDiagnostic; } function reportPrivateInBaseOfClassExpression(propertyName: string) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e352625ab043f..430a2f2b20176 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4831,6 +4831,7 @@ namespace ts { typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs isConstructorDeclaredProperty?: boolean; // Property declared through 'this.x = ...' assignment in constructor tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label + accessibleChainCache?: ESMap; } /* @internal */ @@ -4986,6 +4987,7 @@ namespace ts { isExhaustive?: boolean; // Is node an exhaustive switch statement skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. + serializedTypes?: ESMap; // Collection of types serialized at this location } export const enum TypeFlags { @@ -8118,7 +8120,7 @@ namespace ts { // Called when the symbol writer encounters a symbol to write. Currently only used by the // declaration emitter to help determine if it should patch up the final declaration file // with import statements it previously saw (but chose not to emit). - trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): void; + trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): boolean; reportInaccessibleThisError?(): void; reportPrivateInBaseOfClassExpression?(propertyName: string): void; reportInaccessibleUniqueSymbolError?(): void; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ecc92f8abfd69..6dfdc7b3138eb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -83,7 +83,7 @@ namespace ts { increaseIndent: noop, decreaseIndent: noop, clear: () => str = "", - trackSymbol: noop, + trackSymbol: () => false, reportInaccessibleThisError: noop, reportInaccessibleUniqueSymbolError: noop, reportPrivateInBaseOfClassExpression: noop, @@ -4029,7 +4029,7 @@ namespace ts { reportInaccessibleThisError: noop, reportPrivateInBaseOfClassExpression: noop, reportInaccessibleUniqueSymbolError: noop, - trackSymbol: noop, + trackSymbol: () => false, writeKeyword: write, writeOperator: write, writeParameter: write, diff --git a/src/harness/compilerImpl.ts b/src/harness/compilerImpl.ts index badab5c6cbc8c..41003356fc23a 100644 --- a/src/harness/compilerImpl.ts +++ b/src/harness/compilerImpl.ts @@ -254,7 +254,11 @@ namespace compiler { if (compilerOptions.skipDefaultLibCheck === undefined) compilerOptions.skipDefaultLibCheck = true; if (compilerOptions.noErrorTruncation === undefined) compilerOptions.noErrorTruncation = true; - const preProgram = ts.length(rootFiles) < 100 ? ts.createProgram(rootFiles || [], { ...compilerOptions, configFile: compilerOptions.configFile, traceResolution: false }, host) : undefined; + // pre-emit/post-emit error comparison requires declaration emit twice, which can be slow. If it's unlikely to flag any error consistency issues + // and if the test is running `skipLibCheck` - an indicator that we want the tets to run quickly - skip the before/after error comparison, too + const skipErrorComparison = ts.length(rootFiles) >= 100 || (!!compilerOptions.skipLibCheck && !!compilerOptions.declaration); + + const preProgram = !skipErrorComparison ? ts.createProgram(rootFiles || [], { ...compilerOptions, configFile: compilerOptions.configFile, traceResolution: false }, host) : undefined; const preErrors = preProgram && ts.getPreEmitDiagnostics(preProgram); const program = ts.createProgram(rootFiles || [], compilerOptions, host); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index c6a982546fa22..6900fc6efeb4c 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -18,7 +18,7 @@ namespace ts.codefix { export function getNoopSymbolTrackerWithResolver(context: TypeConstructionContext): SymbolTracker { return { - trackSymbol: noop, + trackSymbol: () => false, moduleResolverHost: getModuleSpecifierResolverHost(context.program, context.host), }; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e5d9b0da77564..e91f962e80eef 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2093,7 +2093,7 @@ namespace ts { increaseIndent: () => { indent++; }, decreaseIndent: () => { indent--; }, clear: resetWriter, - trackSymbol: noop, + trackSymbol: () => false, reportInaccessibleThisError: noop, reportInaccessibleUniqueSymbolError: noop, reportPrivateInBaseOfClassExpression: noop, @@ -2619,6 +2619,7 @@ namespace ts { const res = checker.typeToTypeNode(type, enclosingScope, NodeBuilderFlags.NoTruncation, { trackSymbol: (symbol, declaration, meaning) => { typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible; + return !typeIsAccessible; }, reportInaccessibleThisError: notAccessible, reportPrivateInBaseOfClassExpression: notAccessible, diff --git a/tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.errors.txt b/tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.errors.txt new file mode 100644 index 0000000000000..96ad304af8f15 --- /dev/null +++ b/tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.errors.txt @@ -0,0 +1,42 @@ +tests/cases/compiler/Api.ts(6,5): error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed. +tests/cases/compiler/Api.ts(7,5): error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed. +tests/cases/compiler/Api.ts(8,5): error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed. + + +==== tests/cases/compiler/http-client.ts (0 errors) ==== + type TPromise = Omit, "then" | "catch"> & { + then( + onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: RejectType) => TResult2 | PromiseLike) | undefined | null, + ): TPromise; + catch( + onrejected?: ((reason: RejectType) => TResult | PromiseLike) | undefined | null, + ): TPromise; + }; + + export interface HttpResponse extends Response { + data: D; + error: E; + } + + export class HttpClient { + public request = (): TPromise> => { + return '' as any; + }; + } +==== tests/cases/compiler/Api.ts (3 errors) ==== + import { HttpClient } from "./http-client"; + + export class Api { + constructor(private http: HttpClient) { } + + abc1 = () => this.http.request(); + ~~~~ +!!! error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed. + abc2 = () => this.http.request(); + ~~~~ +!!! error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed. + abc3 = () => this.http.request(); + ~~~~ +!!! error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed. + } \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.js b/tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.js new file mode 100644 index 0000000000000..1b5766d7fc022 --- /dev/null +++ b/tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.js @@ -0,0 +1,77 @@ +//// [tests/cases/compiler/declarationEmitPrivatePromiseLikeInterface.ts] //// + +//// [http-client.ts] +type TPromise = Omit, "then" | "catch"> & { + then( + onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: RejectType) => TResult2 | PromiseLike) | undefined | null, + ): TPromise; + catch( + onrejected?: ((reason: RejectType) => TResult | PromiseLike) | undefined | null, + ): TPromise; +}; + +export interface HttpResponse extends Response { + data: D; + error: E; +} + +export class HttpClient { + public request = (): TPromise> => { + return '' as any; + }; +} +//// [Api.ts] +import { HttpClient } from "./http-client"; + +export class Api { + constructor(private http: HttpClient) { } + + abc1 = () => this.http.request(); + abc2 = () => this.http.request(); + abc3 = () => this.http.request(); +} + +//// [http-client.js] +"use strict"; +exports.__esModule = true; +exports.HttpClient = void 0; +var HttpClient = /** @class */ (function () { + function HttpClient() { + this.request = function () { + return ''; + }; + } + return HttpClient; +}()); +exports.HttpClient = HttpClient; +//// [Api.js] +"use strict"; +exports.__esModule = true; +exports.Api = void 0; +var Api = /** @class */ (function () { + function Api(http) { + var _this = this; + this.http = http; + this.abc1 = function () { return _this.http.request(); }; + this.abc2 = function () { return _this.http.request(); }; + this.abc3 = function () { return _this.http.request(); }; + } + return Api; +}()); +exports.Api = Api; + + +//// [http-client.d.ts] +declare type TPromise = Omit, "then" | "catch"> & { + then(onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: RejectType) => TResult2 | PromiseLike) | undefined | null): TPromise; + catch(onrejected?: ((reason: RejectType) => TResult | PromiseLike) | undefined | null): TPromise; +}; +export interface HttpResponse extends Response { + data: D; + error: E; +} +export declare class HttpClient { + request: () => TPromise, any>; +} +export {}; diff --git a/tests/baselines/reference/declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.types b/tests/baselines/reference/declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.types index b57ceb0b7a4f9..ac96422ad5a87 100644 --- a/tests/baselines/reference/declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.types +++ b/tests/baselines/reference/declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.types @@ -155,7 +155,7 @@ let p2 = p1.deeper({ two: '2' }) >p1.deeper({ two: '2' }) : { result: { one: string; } & { two: string; }; deeper: (child: U) => { result: { one: string; } & { two: string; } & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => any; }; }; }; }; }; }; }; }; }; }; } >p1.deeper : (child: U) => { result: { one: string; } & U; deeper: (child: U) => { result: { one: string; } & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U & U & U; deeper: any; }; }; }; }; }; }; }; }; }; }; } >p1 : { result: { one: string; }; deeper: (child: U) => { result: { one: string; } & U; deeper: (child: U) => { result: { one: string; } & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => any; }; }; }; }; }; }; }; }; }; }; } ->deeper : (child: U) => { result: { one: string; } & U; deeper: (child: U) => { result: { one: string; } & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U & U & U; deeper: any; }; }; }; }; }; }; }; }; }; }; } +>deeper : (child: U) => { result: { one: string; } & U; deeper: (child: U) => { result: { one: string; } & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => any; }; }; }; }; }; }; }; }; }; } >{ two: '2' } : { two: string; } >two : string >'2' : "2" @@ -181,7 +181,7 @@ let p3 = p2.deeper({ three: '3' }) >p2.deeper({ three: '3' }) : { result: { one: string; } & { two: string; } & { three: string; }; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & { three: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => any; }; }; }; }; }; }; }; }; }; }; } >p2.deeper : (child: U) => { result: { one: string; } & { two: string; } & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U & U; deeper: any; }; }; }; }; }; }; }; }; }; }; } >p2 : { result: { one: string; } & { two: string; }; deeper: (child: U) => { result: { one: string; } & { two: string; } & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => any; }; }; }; }; }; }; }; }; }; }; } ->deeper : (child: U) => { result: { one: string; } & { two: string; } & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U & U; deeper: any; }; }; }; }; }; }; }; }; }; }; } +>deeper : (child: U) => { result: { one: string; } & { two: string; } & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U; deeper: (child: U) => { result: { one: string; } & { two: string; } & U & U & U & U & U & U & U & U & U & U; deeper: (child: U) => any; }; }; }; }; }; }; }; }; }; } >{ three: '3' } : { three: string; } >three : string >'3' : "3" diff --git a/tests/cases/compiler/declarationEmitPrivatePromiseLikeInterface.ts b/tests/cases/compiler/declarationEmitPrivatePromiseLikeInterface.ts new file mode 100644 index 0000000000000..093ec0677e907 --- /dev/null +++ b/tests/cases/compiler/declarationEmitPrivatePromiseLikeInterface.ts @@ -0,0 +1,34 @@ +// @declaration: true +// @skipLibCheck: true +// @noTypesAndSymbols: true +// @filename: http-client.ts +type TPromise = Omit, "then" | "catch"> & { + then( + onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike) | undefined | null, + onrejected?: ((reason: RejectType) => TResult2 | PromiseLike) | undefined | null, + ): TPromise; + catch( + onrejected?: ((reason: RejectType) => TResult | PromiseLike) | undefined | null, + ): TPromise; +}; + +export interface HttpResponse extends Response { + data: D; + error: E; +} + +export class HttpClient { + public request = (): TPromise> => { + return '' as any; + }; +} +// @filename: Api.ts +import { HttpClient } from "./http-client"; + +export class Api { + constructor(private http: HttpClient) { } + + abc1 = () => this.http.request(); + abc2 = () => this.http.request(); + abc3 = () => this.http.request(); +} \ No newline at end of file