diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 650797c6dba..45e8efdf512 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -259,7 +259,7 @@ namespace ts { case SyntaxKind.ExportAssignment: return (node).isExportEquals ? "export=" : "default"; case SyntaxKind.BinaryExpression: - switch (getSpecialPropertyAssignmentKind(node)) { + switch (getSpecialPropertyAssignmentKind(node as BinaryExpression)) { case SpecialPropertyAssignmentKind.ModuleExports: // module.exports = ... return "export="; @@ -2017,30 +2017,28 @@ namespace ts { } break; case SyntaxKind.BinaryExpression: - if (isInJavaScriptFile(node)) { - const specialKind = getSpecialPropertyAssignmentKind(node); - switch (specialKind) { - case SpecialPropertyAssignmentKind.ExportsProperty: - bindExportsPropertyAssignment(node); - break; - case SpecialPropertyAssignmentKind.ModuleExports: - bindModuleExportsAssignment(node); - break; - case SpecialPropertyAssignmentKind.PrototypeProperty: - bindPrototypePropertyAssignment(node); - break; - case SpecialPropertyAssignmentKind.ThisProperty: - bindThisPropertyAssignment(node); - break; - case SpecialPropertyAssignmentKind.Property: - bindStaticPropertyAssignment(node); - break; - case SpecialPropertyAssignmentKind.None: - // Nothing to do - break; - default: - Debug.fail("Unknown special property assignment kind"); - } + const specialKind = getSpecialPropertyAssignmentKind(node as BinaryExpression); + switch (specialKind) { + case SpecialPropertyAssignmentKind.ExportsProperty: + bindExportsPropertyAssignment(node); + break; + case SpecialPropertyAssignmentKind.ModuleExports: + bindModuleExportsAssignment(node); + break; + case SpecialPropertyAssignmentKind.PrototypeProperty: + bindPrototypePropertyAssignment(node); + break; + case SpecialPropertyAssignmentKind.ThisProperty: + bindThisPropertyAssignment(node); + break; + case SpecialPropertyAssignmentKind.Property: + bindStaticPropertyAssignment(node); + break; + case SpecialPropertyAssignmentKind.None: + // Nothing to do + break; + default: + Debug.fail("Unknown special property assignment kind"); } return checkStrictModeBinaryExpression(node); case SyntaxKind.CatchClause: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4e55a1c46b5..f9ef54cd362 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8525,16 +8525,9 @@ namespace ts { } } else if (target.flags & TypeFlags.IndexedAccess) { - // if we have indexed access types with identical index types, see if relationship holds for - // the two object types. - if (source.flags & TypeFlags.IndexedAccess && (source).indexType === (target).indexType) { - if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) { - return result; - } - } // A type S is related to a type T[K] if S is related to A[K], where K is string-like and // A is the apparent type of S. - const constraint = getBaseConstraintOfType(target); + const constraint = getConstraintOfType(target); if (constraint) { if (result = isRelatedTo(source, constraint, reportErrors)) { errorInfo = saveErrorInfo; @@ -8581,6 +8574,13 @@ namespace ts { return result; } } + else if (target.flags & TypeFlags.IndexedAccess && (source).indexType === (target).indexType) { + // if we have indexed access types with identical index types, see if relationship holds for + // the two object types. + if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) { + return result; + } + } } else { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { @@ -11929,8 +11929,8 @@ namespace ts { // If this is a function in a JS file, it might be a class method. Check if it's the RHS // of a x.prototype.y = function [name]() { .... } if (container.kind === SyntaxKind.FunctionExpression && - isInJavaScriptFile(container.parent) && - getSpecialPropertyAssignmentKind(container.parent) === SpecialPropertyAssignmentKind.PrototypeProperty) { + container.parent.kind === SyntaxKind.BinaryExpression && + getSpecialPropertyAssignmentKind(container.parent as BinaryExpression) === SpecialPropertyAssignmentKind.PrototypeProperty) { // Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container') const className = (((container.parent as BinaryExpression) // x.prototype.y = f .left as PropertyAccessExpression) // x.prototype.y @@ -21641,7 +21641,7 @@ namespace ts { } function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) { - const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent); + const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent as BinaryExpression); switch (specialPropertyAssignmentKind) { case SpecialPropertyAssignmentKind.ExportsProperty: case SpecialPropertyAssignmentKind.PrototypeProperty: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 33cb0d8f489..f0916855986 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1450,13 +1450,10 @@ namespace ts { /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder - export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind { + export function getSpecialPropertyAssignmentKind(expression: ts.BinaryExpression): SpecialPropertyAssignmentKind { if (!isInJavaScriptFile(expression)) { return SpecialPropertyAssignmentKind.None; } - if (expression.kind !== SyntaxKind.BinaryExpression) { - return SpecialPropertyAssignmentKind.None; - } const expr = expression; if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || expr.left.kind !== SyntaxKind.PropertyAccessExpression) { return SpecialPropertyAssignmentKind.None; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 356a7a8962d..b2fc4322242 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -176,11 +176,11 @@ namespace ts.projectSystem { } }; - export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken) { + export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken, throttleWaitMilliseconds?: number) { if (typingsInstaller === undefined) { typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host); } - return new TestSession(host, cancellationToken || server.nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ projectServiceEventHandler !== undefined, projectServiceEventHandler); + return new TestSession(host, cancellationToken || server.nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ projectServiceEventHandler !== undefined, projectServiceEventHandler, throttleWaitMilliseconds); } export interface CreateProjectServiceParameters { @@ -547,6 +547,49 @@ namespace ts.projectSystem { readonly getEnvironmentVariable = notImplemented; } + /** + * Test server cancellation token used to mock host token cancellation requests. + * The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls + * should be made before canceling the token. The id of the request to cancel should be set with + * setRequestToCancel(); + */ + export class TestServerCancellationToken implements server.ServerCancellationToken { + private currentId = -1; + private requestToCancel = -1; + private isCancellationRequestedCount = 0; + + constructor(private cancelAfterRequest = 0) { + } + + setRequest(requestId: number) { + this.currentId = requestId; + } + + setRequestToCancel(requestId: number) { + this.resetToken(); + this.requestToCancel = requestId; + } + + resetRequest(requestId: number) { + assert.equal(requestId, this.currentId, "unexpected request id in cancellation"); + this.currentId = undefined; + } + + isCancellationRequested() { + this.isCancellationRequestedCount++; + // If the request id is the request to cancel and isCancellationRequestedCount + // has been met then cancel the request. Ex: cancel the request if it is a + // nav bar request & isCancellationRequested() has already been called three times. + return this.requestToCancel === this.currentId && this.isCancellationRequestedCount >= this.cancelAfterRequest; + } + + resetToken() { + this.currentId = -1; + this.isCancellationRequestedCount = 0; + this.requestToCancel = -1; + } + } + export function makeSessionRequest(command: string, args: T) { const newRequest: protocol.Request = { seq: 0, @@ -3384,6 +3427,7 @@ namespace ts.projectSystem { }, resetRequest: noop }; + const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken); expectedRequestId = session.getNextSeq(); @@ -3422,22 +3466,7 @@ namespace ts.projectSystem { }) }; - let requestToCancel = -1; - const cancellationToken: server.ServerCancellationToken = (function(){ - let currentId: number; - return { - setRequest(requestId) { - currentId = requestId; - }, - resetRequest(requestId) { - assert.equal(requestId, currentId, "unexpected request id in cancellation"); - currentId = undefined; - }, - isCancellationRequested() { - return requestToCancel === currentId; - } - }; - })(); + const cancellationToken = new TestServerCancellationToken(); const host = createServerHost([f1, config]); const session = createSession(host, /*typingsInstaller*/ undefined, () => {}, cancellationToken); { @@ -3472,13 +3501,13 @@ namespace ts.projectSystem { host.clearOutput(); // cancel previously issued Geterr - requestToCancel = getErrId; + cancellationToken.setRequestToCancel(getErrId); host.runQueuedTimeoutCallbacks(); assert.equal(host.getOutput().length, 1, "expect 1 message"); verifyRequestCompleted(getErrId, 0); - requestToCancel = -1; + cancellationToken.resetToken(); } { const getErrId = session.getNextSeq(); @@ -3495,12 +3524,12 @@ namespace ts.projectSystem { assert.equal(e1.event, "syntaxDiag"); host.clearOutput(); - requestToCancel = getErrId; + cancellationToken.setRequestToCancel(getErrId); host.runQueuedImmediateCallbacks(); assert.equal(host.getOutput().length, 1, "expect 1 message"); verifyRequestCompleted(getErrId, 0); - requestToCancel = -1; + cancellationToken.resetToken(); } { const getErrId = session.getNextSeq(); @@ -3523,7 +3552,7 @@ namespace ts.projectSystem { assert.equal(e2.event, "semanticDiag"); verifyRequestCompleted(getErrId, 1); - requestToCancel = -1; + cancellationToken.resetToken(); } { const getErr1 = session.getNextSeq(); @@ -3558,6 +3587,68 @@ namespace ts.projectSystem { return JSON.parse(server.extractMessage(host.getOutput()[n])); } }); + it("Lower priority tasks are cancellable", () => { + const f1 = { + path: "/a/app.ts", + content: `{ let x = 1; } var foo = "foo"; var bar = "bar"; var fooBar = "fooBar";` + }; + const config = { + path: "/a/tsconfig.json", + content: JSON.stringify({ + compilerOptions: {} + }) + }; + const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3); + const host = createServerHost([f1, config]); + const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken, /*throttleWaitMilliseconds*/ 0); + { + session.executeCommandSeq({ + command: "open", + arguments: { file: f1.path } + }); + + // send navbar request (normal priority) + session.executeCommandSeq({ + command: "navbar", + arguments: { file: f1.path } + }); + + // ensure the nav bar request can be canceled + verifyExecuteCommandSeqIsCancellable({ + command: "navbar", + arguments: { file: f1.path } + }); + + // send outlining spans request (normal priority) + session.executeCommandSeq({ + command: "outliningSpans", + arguments: { file: f1.path } + }); + + // ensure the outlining spans request can be canceled + verifyExecuteCommandSeqIsCancellable({ + command: "outliningSpans", + arguments: { file: f1.path } + }); + } + + function verifyExecuteCommandSeqIsCancellable(request: Partial) { + // Set the next request to be cancellable + // The cancellation token will cancel the request the third time + // isCancellationRequested() is called. + cancellationToken.setRequestToCancel(session.getNextSeq()); + let operationCanceledExceptionThrown = false; + + try { + session.executeCommandSeq(request); + } + catch (e) { + assert(e instanceof OperationCanceledException); + operationCanceledExceptionThrown = true; + } + assert(operationCanceledExceptionThrown, "Operation Canceled Exception not thrown for request: " + JSON.stringify(request)); + } + }); }); describe("occurence highlight on string", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 8af6fe5d347..30c7a87dc74 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -271,7 +271,8 @@ namespace ts.server { public readonly cancellationToken: HostCancellationToken, public readonly useSingleInferredProject: boolean, readonly typingsInstaller: ITypingsInstaller = nullTypingsInstaller, - private readonly eventHandler?: ProjectServiceEventHandler) { + private readonly eventHandler?: ProjectServiceEventHandler, + public readonly throttleWaitMilliseconds?: number) { Debug.assert(!!host.createHash, "'ServerHost.createHash' is required for ProjectService"); diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index eee80d82e7f..ec655c545ea 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -16,6 +16,7 @@ namespace ts.server { readonly realpath?: (path: string) => string; constructor(private readonly host: ServerHost, private readonly project: Project, private readonly cancellationToken: HostCancellationToken) { + this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds); this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); if (host.trace) { diff --git a/src/server/session.ts b/src/server/session.ts index 337aab1c3fb..7161bdc04dd 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -338,7 +338,8 @@ namespace ts.server { private hrtime: (start?: number[]) => number[], protected logger: Logger, protected readonly canUseEvents: boolean, - eventHandler?: ProjectServiceEventHandler) { + eventHandler?: ProjectServiceEventHandler, + private readonly throttleWaitMilliseconds?: number) { this.eventHander = canUseEvents ? eventHandler || (event => this.defaultEventHandler(event)) @@ -353,7 +354,7 @@ namespace ts.server { isCancellationRequested: () => cancellationToken.isCancellationRequested() }; this.errorCheck = new MultistepOperation(multistepOperationHost); - this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander); + this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander, this.throttleWaitMilliseconds); this.gcTimer = new GcTimer(host, /*delay*/ 7000, logger); } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 25059d1f57d..7a2508fcfd6 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -2,6 +2,36 @@ /* @internal */ namespace ts.NavigationBar { + /** + * Matches all whitespace characters in a string. Eg: + * + * "app. + * + * onactivated" + * + * matches because of the newline, whereas + * + * "app.onactivated" + * + * does not match. + */ + const whiteSpaceRegex = /\s+/g; + + // Keep sourceFile handy so we don't have to search for it every time we need to call `getText`. + let curCancellationToken: CancellationToken; + let curSourceFile: SourceFile; + + /** + * For performance, we keep navigation bar parents on a stack rather than passing them through each recursion. + * `parent` is the current parent and is *not* stored in parentsStack. + * `startNode` sets a new parent and `endNode` returns to the previous parent. + */ + let parentsStack: NavigationBarNode[] = []; + let parent: NavigationBarNode; + + // NavigationBarItem requires an array, but will not mutate it, so just give it this for performance. + let emptyChildItemArray: NavigationBarItem[] = []; + /** * Represents a navigation bar item and its children. * The returned NavigationBarItem is more complicated and doesn't include 'parent', so we use these to do work before converting. @@ -14,22 +44,36 @@ namespace ts.NavigationBar { indent: number; // # of parents } - export function getNavigationBarItems(sourceFile: SourceFile): NavigationBarItem[] { + export function getNavigationBarItems(sourceFile: SourceFile, cancellationToken: CancellationToken): NavigationBarItem[] { + curCancellationToken = cancellationToken; curSourceFile = sourceFile; - const result = map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem); - curSourceFile = undefined; - return result; + try { + return map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem); + } + finally { + reset(); + } } - export function getNavigationTree(sourceFile: SourceFile): NavigationTree { + export function getNavigationTree(sourceFile: SourceFile, cancellationToken: CancellationToken): NavigationTree { + curCancellationToken = cancellationToken; curSourceFile = sourceFile; - const result = convertToTree(rootNavigationBarNode(sourceFile)); - curSourceFile = undefined; - return result; + try { + return convertToTree(rootNavigationBarNode(sourceFile)); + } + finally { + reset(); + } + } + + function reset() { + curSourceFile = undefined; + curCancellationToken = undefined; + parentsStack = []; + parent = undefined; + emptyChildItemArray = []; } - // Keep sourceFile handy so we don't have to search for it every time we need to call `getText`. - let curSourceFile: SourceFile; function nodeText(node: Node): string { return node.getText(curSourceFile); } @@ -47,14 +91,6 @@ namespace ts.NavigationBar { } } - /* - For performance, we keep navigation bar parents on a stack rather than passing them through each recursion. - `parent` is the current parent and is *not* stored in parentsStack. - `startNode` sets a new parent and `endNode` returns to the previous parent. - */ - const parentsStack: NavigationBarNode[] = []; - let parent: NavigationBarNode; - function rootNavigationBarNode(sourceFile: SourceFile): NavigationBarNode { Debug.assert(!parentsStack.length); const root: NavigationBarNode = { node: sourceFile, additionalNodes: undefined, parent: undefined, children: undefined, indent: 0 }; @@ -111,6 +147,8 @@ namespace ts.NavigationBar { /** Look for navigation bar items in node's subtree, adding them to the current `parent`. */ function addChildrenRecursively(node: Node): void { + curCancellationToken.throwIfCancellationRequested(); + if (!node || isToken(node)) { return; } @@ -487,9 +525,6 @@ namespace ts.NavigationBar { } } - // NavigationBarItem requires an array, but will not mutate it, so just give it this for performance. - const emptyChildItemArray: NavigationBarItem[] = []; - function convertToTree(n: NavigationBarNode): NavigationTree { return { text: getItemName(n.node), @@ -610,19 +645,4 @@ namespace ts.NavigationBar { function isFunctionOrClassExpression(node: Node): boolean { return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.ClassExpression; } - - /** - * Matches all whitespace characters in a string. Eg: - * - * "app. - * - * onactivated" - * - * matches because of the newline, whereas - * - * "app.onactivated" - * - * does not match. - */ - const whiteSpaceRegex = /\s+/g; } diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 43054a10f5d..510f78c3f39 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts.OutliningElementsCollector { - export function collectElements(sourceFile: SourceFile): OutliningSpan[] { + export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] { const elements: OutliningSpan[] = []; const collapseText = "..."; @@ -38,6 +38,7 @@ namespace ts.OutliningElementsCollector { let singleLineCommentCount = 0; for (const currentComment of comments) { + cancellationToken.throwIfCancellationRequested(); // For single line comments, combine consecutive ones (2 or more) into // a single span from the start of the first till the end of the last @@ -84,6 +85,7 @@ namespace ts.OutliningElementsCollector { let depth = 0; const maxDepth = 20; function walk(n: Node): void { + cancellationToken.throwIfCancellationRequested(); if (depth > maxDepth) { return; } diff --git a/src/services/services.ts b/src/services/services.ts index e295022063d..68429cb957d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -982,6 +982,36 @@ namespace ts { } } + /* @internal */ + /** A cancellation that throttles calls to the host */ + export class ThrottledCancellationToken implements CancellationToken { + // Store when we last tried to cancel. Checking cancellation can be expensive (as we have + // to marshall over to the host layer). So we only bother actually checking once enough + // time has passed. + private lastCancellationCheckTime = 0; + + constructor(private hostCancellationToken: HostCancellationToken, private readonly throttleWaitMilliseconds = 20) { + } + + public isCancellationRequested(): boolean { + const time = timestamp(); + const duration = Math.abs(time - this.lastCancellationCheckTime); + if (duration >= this.throttleWaitMilliseconds) { + // Check no more than once every throttle wait milliseconds + this.lastCancellationCheckTime = time; + return this.hostCancellationToken.isCancellationRequested(); + } + + return false; + } + + public throwIfCancellationRequested(): void { + if (this.isCancellationRequested()) { + throw new OperationCanceledException(); + } + } + } + export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService { @@ -1553,11 +1583,11 @@ namespace ts { } function getNavigationBarItems(fileName: string): NavigationBarItem[] { - return NavigationBar.getNavigationBarItems(syntaxTreeCache.getCurrentSourceFile(fileName)); + return NavigationBar.getNavigationBarItems(syntaxTreeCache.getCurrentSourceFile(fileName), cancellationToken); } function getNavigationTree(fileName: string): NavigationTree { - return NavigationBar.getNavigationTree(syntaxTreeCache.getCurrentSourceFile(fileName)); + return NavigationBar.getNavigationTree(syntaxTreeCache.getCurrentSourceFile(fileName), cancellationToken); } function isTsOrTsxFile(fileName: string): boolean { @@ -1596,7 +1626,7 @@ namespace ts { function getOutliningSpans(fileName: string): OutliningSpan[] { // doesn't use compiler - no need to synchronize with host const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return OutliningElementsCollector.collectElements(sourceFile); + return OutliningElementsCollector.collectElements(sourceFile, cancellationToken); } function getBraceMatchingAtPosition(fileName: string, position: number) { diff --git a/src/services/shims.ts b/src/services/shims.ts index 6fe9aed144e..de2fd39b8a8 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -469,29 +469,6 @@ namespace ts { } } - /** A cancellation that throttles calls to the host */ - class ThrottledCancellationToken implements HostCancellationToken { - // Store when we last tried to cancel. Checking cancellation can be expensive (as we have - // to marshall over to the host layer). So we only bother actually checking once enough - // time has passed. - private lastCancellationCheckTime = 0; - - constructor(private hostCancellationToken: HostCancellationToken) { - } - - public isCancellationRequested(): boolean { - const time = timestamp(); - const duration = Math.abs(time - this.lastCancellationCheckTime); - if (duration > 10) { - // Check no more than once every 10 ms. - this.lastCancellationCheckTime = time; - return this.hostCancellationToken.isCancellationRequested(); - } - - return false; - } - } - export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost { public directoryExists: (directoryName: string) => boolean; diff --git a/tests/baselines/reference/indexedAccessRelation.js b/tests/baselines/reference/indexedAccessRelation.js new file mode 100644 index 00000000000..c58c214e69e --- /dev/null +++ b/tests/baselines/reference/indexedAccessRelation.js @@ -0,0 +1,56 @@ +//// [indexedAccessRelation.ts] +// Repro from #14723 + +class Component { + setState(state: Pick) {} +} + +export interface State { + a?: T; +} + +class Foo {} + +class Comp extends Component> +{ + foo(a: T) { + this.setState({ a: a }); + } +} + + +//// [indexedAccessRelation.js] +// Repro from #14723 +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var Component = (function () { + function Component() { + } + Component.prototype.setState = function (state) { }; + return Component; +}()); +var Foo = (function () { + function Foo() { + } + return Foo; +}()); +var Comp = (function (_super) { + __extends(Comp, _super); + function Comp() { + return _super !== null && _super.apply(this, arguments) || this; + } + Comp.prototype.foo = function (a) { + this.setState({ a: a }); + }; + return Comp; +}(Component)); diff --git a/tests/baselines/reference/indexedAccessRelation.symbols b/tests/baselines/reference/indexedAccessRelation.symbols new file mode 100644 index 00000000000..40fa52652f9 --- /dev/null +++ b/tests/baselines/reference/indexedAccessRelation.symbols @@ -0,0 +1,53 @@ +=== tests/cases/compiler/indexedAccessRelation.ts === +// Repro from #14723 + +class Component { +>Component : Symbol(Component, Decl(indexedAccessRelation.ts, 0, 0)) +>S : Symbol(S, Decl(indexedAccessRelation.ts, 2, 16)) + + setState(state: Pick) {} +>setState : Symbol(Component.setState, Decl(indexedAccessRelation.ts, 2, 20)) +>K : Symbol(K, Decl(indexedAccessRelation.ts, 3, 13)) +>S : Symbol(S, Decl(indexedAccessRelation.ts, 2, 16)) +>state : Symbol(state, Decl(indexedAccessRelation.ts, 3, 32)) +>Pick : Symbol(Pick, Decl(lib.d.ts, --, --)) +>S : Symbol(S, Decl(indexedAccessRelation.ts, 2, 16)) +>K : Symbol(K, Decl(indexedAccessRelation.ts, 3, 13)) +} + +export interface State { +>State : Symbol(State, Decl(indexedAccessRelation.ts, 4, 1)) +>T : Symbol(T, Decl(indexedAccessRelation.ts, 6, 23)) + + a?: T; +>a : Symbol(State.a, Decl(indexedAccessRelation.ts, 6, 27)) +>T : Symbol(T, Decl(indexedAccessRelation.ts, 6, 23)) +} + +class Foo {} +>Foo : Symbol(Foo, Decl(indexedAccessRelation.ts, 8, 1)) + +class Comp extends Component> +>Comp : Symbol(Comp, Decl(indexedAccessRelation.ts, 10, 12)) +>T : Symbol(T, Decl(indexedAccessRelation.ts, 12, 11)) +>Foo : Symbol(Foo, Decl(indexedAccessRelation.ts, 8, 1)) +>S : Symbol(S, Decl(indexedAccessRelation.ts, 12, 25)) +>Component : Symbol(Component, Decl(indexedAccessRelation.ts, 0, 0)) +>S : Symbol(S, Decl(indexedAccessRelation.ts, 12, 25)) +>State : Symbol(State, Decl(indexedAccessRelation.ts, 4, 1)) +>T : Symbol(T, Decl(indexedAccessRelation.ts, 12, 11)) +{ + foo(a: T) { +>foo : Symbol(Comp.foo, Decl(indexedAccessRelation.ts, 13, 1)) +>a : Symbol(a, Decl(indexedAccessRelation.ts, 14, 8)) +>T : Symbol(T, Decl(indexedAccessRelation.ts, 12, 11)) + + this.setState({ a: a }); +>this.setState : Symbol(Component.setState, Decl(indexedAccessRelation.ts, 2, 20)) +>this : Symbol(Comp, Decl(indexedAccessRelation.ts, 10, 12)) +>setState : Symbol(Component.setState, Decl(indexedAccessRelation.ts, 2, 20)) +>a : Symbol(a, Decl(indexedAccessRelation.ts, 15, 23)) +>a : Symbol(a, Decl(indexedAccessRelation.ts, 14, 8)) + } +} + diff --git a/tests/baselines/reference/indexedAccessRelation.types b/tests/baselines/reference/indexedAccessRelation.types new file mode 100644 index 00000000000..2564a82702d --- /dev/null +++ b/tests/baselines/reference/indexedAccessRelation.types @@ -0,0 +1,55 @@ +=== tests/cases/compiler/indexedAccessRelation.ts === +// Repro from #14723 + +class Component { +>Component : Component +>S : S + + setState(state: Pick) {} +>setState : (state: Pick) => void +>K : K +>S : S +>state : Pick +>Pick : Pick +>S : S +>K : K +} + +export interface State { +>State : State +>T : T + + a?: T; +>a : T +>T : T +} + +class Foo {} +>Foo : Foo + +class Comp extends Component> +>Comp : Comp +>T : T +>Foo : Foo +>S : S +>Component : Component> +>S : S +>State : State +>T : T +{ + foo(a: T) { +>foo : (a: T) => void +>a : T +>T : T + + this.setState({ a: a }); +>this.setState({ a: a }) : void +>this.setState : )>(state: Pick, K>) => void +>this : this +>setState : )>(state: Pick, K>) => void +>{ a: a } : { a: T; } +>a : T +>a : T + } +} + diff --git a/tests/baselines/reference/mappedTypeRelationships.errors.txt b/tests/baselines/reference/mappedTypeRelationships.errors.txt index bc26bb272f5..ec2bacbe663 100644 --- a/tests/baselines/reference/mappedTypeRelationships.errors.txt +++ b/tests/baselines/reference/mappedTypeRelationships.errors.txt @@ -1,39 +1,71 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(12,5): error TS2322: Type 'T[keyof T]' is not assignable to type 'U[keyof T]'. Type 'T[string]' is not assignable to type 'U[keyof T]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[keyof T]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(17,5): error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. Type 'T[string]' is not assignable to type 'U[K]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[K]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(21,5): error TS2536: Type 'keyof U' cannot be used to index type 'T'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(22,5): error TS2322: Type 'T[keyof U]' is not assignable to type 'U[keyof U]'. Type 'T[string]' is not assignable to type 'U[keyof U]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[keyof U]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(22,12): error TS2536: Type 'keyof U' cannot be used to index type 'T'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(26,5): error TS2536: Type 'K' cannot be used to index type 'T'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(27,5): error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. Type 'T[string]' is not assignable to type 'U[K]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[K]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(27,12): error TS2536: Type 'K' cannot be used to index type 'T'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(31,5): error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'T[keyof T]'. Type 'undefined' is not assignable to type 'T[keyof T]'. + Type 'undefined' is not assignable to type 'T[string]'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(36,5): error TS2322: Type 'T[K] | undefined' is not assignable to type 'T[K]'. Type 'undefined' is not assignable to type 'T[K]'. + Type 'undefined' is not assignable to type 'T[string]'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(41,5): error TS2322: Type 'U[keyof T] | undefined' is not assignable to type 'T[keyof T]'. Type 'undefined' is not assignable to type 'T[keyof T]'. + Type 'undefined' is not assignable to type 'T[string]'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(42,5): error TS2322: Type 'T[keyof T]' is not assignable to type 'U[keyof T] | undefined'. Type 'T[string]' is not assignable to type 'U[keyof T] | undefined'. Type 'T[string]' is not assignable to type 'U[keyof T]'. - Type 'T[keyof T]' is not assignable to type 'U[keyof T]'. - Type 'T[string]' is not assignable to type 'U[keyof T]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[keyof T]' is not assignable to type 'U[keyof T]'. + Type 'T[string]' is not assignable to type 'U[keyof T]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[keyof T]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(46,5): error TS2322: Type 'U[K] | undefined' is not assignable to type 'T[K]'. Type 'undefined' is not assignable to type 'T[K]'. + Type 'undefined' is not assignable to type 'T[string]'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(47,5): error TS2322: Type 'T[K]' is not assignable to type 'U[K] | undefined'. Type 'T[string]' is not assignable to type 'U[K] | undefined'. Type 'T[string]' is not assignable to type 'U[K]'. - Type 'T[K]' is not assignable to type 'U[K]'. - Type 'T[string]' is not assignable to type 'U[K]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[K]' is not assignable to type 'U[K]'. + Type 'T[string]' is not assignable to type 'U[K]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[K]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(52,5): error TS2542: Index signature in type 'Readonly' only permits reading. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(57,5): error TS2542: Index signature in type 'Readonly' only permits reading. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(62,5): error TS2542: Index signature in type 'Readonly' only permits reading. @@ -44,7 +76,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(126,5): error TS tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(142,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'. Type 'T[P]' is not assignable to type 'U[P]'. Type 'T[string]' is not assignable to type 'U[P]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[P]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(147,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof U]: U[P]; }'. Type 'keyof U' is not assignable to type 'keyof T'. tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(152,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in keyof T]: T[P]; }'. @@ -56,7 +92,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(162,5): error TS tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'. Type 'T[P]' is not assignable to type 'U[P]'. Type 'T[string]' is not assignable to type 'U[P]'. - Type 'T' is not assignable to type 'U'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. + Type 'T[P]' is not assignable to type 'U[string]'. + Type 'T[string]' is not assignable to type 'U[string]'. + Type 'T' is not assignable to type 'U'. ==== tests/cases/conformance/types/mapped/mappedTypeRelationships.ts (27 errors) ==== @@ -75,7 +115,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'T[keyof T]' is not assignable to type 'U[keyof T]'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[keyof T]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. } function f4(x: T, y: U, k: K) { @@ -84,7 +128,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[K]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[K]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. } function f5(x: T, y: U, k: keyof U) { @@ -95,7 +143,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'T[keyof U]' is not assignable to type 'U[keyof U]'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[keyof U]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[keyof U]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. ~~~~ !!! error TS2536: Type 'keyof U' cannot be used to index type 'T'. } @@ -108,7 +160,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[K]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[K]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. ~~~~ !!! error TS2536: Type 'K' cannot be used to index type 'T'. } @@ -118,6 +174,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'T[keyof T] | undefined' is not assignable to type 'T[keyof T]'. !!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T]'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T[string]'. y[k] = x[k]; } @@ -126,6 +183,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'T[K] | undefined' is not assignable to type 'T[K]'. !!! error TS2322: Type 'undefined' is not assignable to type 'T[K]'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T[string]'. y[k] = x[k]; } @@ -134,14 +192,21 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'U[keyof T] | undefined' is not assignable to type 'T[keyof T]'. !!! error TS2322: Type 'undefined' is not assignable to type 'T[keyof T]'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T[string]'. y[k] = x[k]; // Error ~~~~ !!! error TS2322: Type 'T[keyof T]' is not assignable to type 'U[keyof T] | undefined'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[keyof T] | undefined'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[keyof T]'. -!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'U[keyof T]'. -!!! error TS2322: Type 'T[string]' is not assignable to type 'U[keyof T]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'U[keyof T]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[keyof T]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. } function f13(x: T, y: Partial, k: K) { @@ -149,14 +214,21 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS ~~~~ !!! error TS2322: Type 'U[K] | undefined' is not assignable to type 'T[K]'. !!! error TS2322: Type 'undefined' is not assignable to type 'T[K]'. +!!! error TS2322: Type 'undefined' is not assignable to type 'T[string]'. y[k] = x[k]; // Error ~~~~ !!! error TS2322: Type 'T[K]' is not assignable to type 'U[K] | undefined'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[K] | undefined'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[K]'. -!!! error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. -!!! error TS2322: Type 'T[string]' is not assignable to type 'U[K]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[K]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[K]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. } function f20(x: T, y: Readonly, k: keyof T) { @@ -270,7 +342,11 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS !!! error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'. !!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[P]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[P]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. } function f72(x: { [P in keyof T]: T[P] }, y: { [P in keyof U]: U[P] }) { @@ -312,6 +388,10 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(167,5): error TS !!! error TS2322: Type '{ [P in K]: T[P]; }' is not assignable to type '{ [P in K]: U[P]; }'. !!! error TS2322: Type 'T[P]' is not assignable to type 'U[P]'. !!! error TS2322: Type 'T[string]' is not assignable to type 'U[P]'. -!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. +!!! error TS2322: Type 'T[P]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. } \ No newline at end of file diff --git a/tests/cases/compiler/indexedAccessRelation.ts b/tests/cases/compiler/indexedAccessRelation.ts new file mode 100644 index 00000000000..ccfcd559e20 --- /dev/null +++ b/tests/cases/compiler/indexedAccessRelation.ts @@ -0,0 +1,18 @@ +// Repro from #14723 + +class Component { + setState(state: Pick) {} +} + +export interface State { + a?: T; +} + +class Foo {} + +class Comp extends Component> +{ + foo(a: T) { + this.setState({ a: a }); + } +}