diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 282765dcc7b1d..c85414fbcef0d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6631,6 +6631,9 @@ namespace ts { else if (type.flags & TypeFlags.Intersection) { return getIntersectionType(map((type).types, t => getTypeWithThisArgument(t, thisArgument, needApparentType))); } + else if (type.flags & TypeFlags.TypeParameter && thisArgument && (type as TypeParameter).isThisType) { + return getTypeWithThisArgument(createTypeReference(globalThisType, [type]), needApparentType ? instantiateType(thisArgument, createTypeMapper([type], [getApparentType(type)])) : thisArgument, needApparentType); + } return needApparentType ? getApparentType(type) : type; } @@ -6685,6 +6688,23 @@ namespace ts { } function resolveTypeReferenceMembers(type: TypeReference): void { + + if (type.target === globalThisType && type.typeArguments && type.typeArguments[0].flags & TypeFlags.TypeParameter && + (type.typeArguments[0] as TypeParameter).isThisType && type.typeArguments[1]) { + if (type.typeArguments[1].flags & TypeFlags.StructuredType) { + const instantiatedTarget = instantiateType(type.typeArguments[1], createTypeMapper([type.typeArguments[0]], [type.typeArguments[1]])); + const props = getPropertiesOfType(instantiatedTarget); + const calls = getSignaturesOfType(instantiatedTarget, SignatureKind.Call); + const ctors = getSignaturesOfType(instantiatedTarget, SignatureKind.Construct); + const strIndex = getIndexInfoOfType(instantiatedTarget, IndexKind.String); + const numIndex = getIndexInfoOfType(instantiatedTarget, IndexKind.Number); + setStructuredTypeMembers(type, createMapFromEntries(props.map(p => [p.escapedName, p]) as [string, Symbol][]) as SymbolTable, calls, ctors, strIndex, numIndex); + } + else { + setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined); + } + return; + } const source = resolveDeclaredMembers(type.target); const typeParameters = concatenate(source.typeParameters!, [source.thisType!]); const typeArguments = type.typeArguments && type.typeArguments.length === typeParameters.length ? diff --git a/tests/baselines/reference/chainedThisRefinements.js b/tests/baselines/reference/chainedThisRefinements.js new file mode 100644 index 0000000000000..ca743711b421b --- /dev/null +++ b/tests/baselines/reference/chainedThisRefinements.js @@ -0,0 +1,48 @@ +//// [chainedThisRefinements.ts] +class Builder { + private _class: undefined; + withFoo() { + return this as this & { foo: T } + } + withBar() { + return this as this & { bar: T } + } + withFooBar() { + this.withFoo().withBar().foo; + this.withFoo().withBar().bar; + return this.withFoo().withBar(); + } +} + +declare var f: {foo: number}; +new Builder().withFoo().withBar().foo; +new Builder().withFoo().withBar().bar; +f = new Builder().withFoo().withBar(); +new Builder().withFooBar().foo; +new Builder().withFooBar().bar; +f = new Builder().withFooBar(); + + +//// [chainedThisRefinements.js] +var Builder = /** @class */ (function () { + function Builder() { + } + Builder.prototype.withFoo = function () { + return this; + }; + Builder.prototype.withBar = function () { + return this; + }; + Builder.prototype.withFooBar = function () { + this.withFoo().withBar().foo; + this.withFoo().withBar().bar; + return this.withFoo().withBar(); + }; + return Builder; +}()); +new Builder().withFoo().withBar().foo; +new Builder().withFoo().withBar().bar; +f = new Builder().withFoo().withBar(); +new Builder().withFooBar().foo; +new Builder().withFooBar().bar; +f = new Builder().withFooBar(); diff --git a/tests/baselines/reference/chainedThisRefinements.symbols b/tests/baselines/reference/chainedThisRefinements.symbols new file mode 100644 index 0000000000000..668fd977418f7 --- /dev/null +++ b/tests/baselines/reference/chainedThisRefinements.symbols @@ -0,0 +1,112 @@ +=== tests/cases/compiler/chainedThisRefinements.ts === +class Builder { +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) + + private _class: undefined; +>_class : Symbol(Builder._class, Decl(chainedThisRefinements.ts, 0, 15)) + + withFoo() { +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 2, 12)) + + return this as this & { foo: T } +>this : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 2, 12)) + } + withBar() { +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 5, 12)) + + return this as this & { bar: T } +>this : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 5, 12)) + } + withFooBar() { +>withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) + + this.withFoo().withBar().foo; +>this.withFoo().withBar().foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) +>this.withFoo().withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>this.withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>this : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) +>foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) + + this.withFoo().withBar().bar; +>this.withFoo().withBar().bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) +>this.withFoo().withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>this.withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>this : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) +>bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) + + return this.withFoo().withBar(); +>this.withFoo().withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>this.withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>this : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15)) + } +} + +declare var f: {foo: number}; +>f : Symbol(f, Decl(chainedThisRefinements.ts, 15, 11)) +>foo : Symbol(foo, Decl(chainedThisRefinements.ts, 15, 16)) + +new Builder().withFoo().withBar().foo; +>new Builder().withFoo().withBar().foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) +>new Builder().withFoo().withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>new Builder().withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) + +new Builder().withFoo().withBar().bar; +>new Builder().withFoo().withBar().bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) +>new Builder().withFoo().withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>new Builder().withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) + +f = new Builder().withFoo().withBar(); +>f : Symbol(f, Decl(chainedThisRefinements.ts, 15, 11)) +>new Builder().withFoo().withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) +>new Builder().withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFoo : Symbol(Builder.withFoo, Decl(chainedThisRefinements.ts, 1, 30)) +>withBar : Symbol(Builder.withBar, Decl(chainedThisRefinements.ts, 4, 5)) + +new Builder().withFooBar().foo; +>new Builder().withFooBar().foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) +>new Builder().withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) +>foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31)) + +new Builder().withFooBar().bar; +>new Builder().withFooBar().bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) +>new Builder().withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) +>bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31)) + +f = new Builder().withFooBar(); +>f : Symbol(f, Decl(chainedThisRefinements.ts, 15, 11)) +>new Builder().withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) +>Builder : Symbol(Builder, Decl(chainedThisRefinements.ts, 0, 0)) +>withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5)) + diff --git a/tests/baselines/reference/chainedThisRefinements.types b/tests/baselines/reference/chainedThisRefinements.types new file mode 100644 index 0000000000000..6b03ee427d365 --- /dev/null +++ b/tests/baselines/reference/chainedThisRefinements.types @@ -0,0 +1,126 @@ +=== tests/cases/compiler/chainedThisRefinements.ts === +class Builder { +>Builder : Builder + + private _class: undefined; +>_class : undefined + + withFoo() { +>withFoo : () => this & { foo: T; } + + return this as this & { foo: T } +>this as this & { foo: T } : this & { foo: T; } +>this : this +>foo : T + } + withBar() { +>withBar : () => this & { bar: T; } + + return this as this & { bar: T } +>this as this & { bar: T } : this & { bar: T; } +>this : this +>bar : T + } + withFooBar() { +>withFooBar : () => Builder & { foo: T; } & { bar: T; } + + this.withFoo().withBar().foo; +>this.withFoo().withBar().foo : T +>this.withFoo().withBar() : Builder & { foo: T; } & { bar: T; } +>this.withFoo().withBar : () => Builder & { foo: T; } & { bar: T; } +>this.withFoo() : this & { foo: T; } +>this.withFoo : () => this & { foo: T; } +>this : this +>withFoo : () => this & { foo: T; } +>withBar : () => Builder & { foo: T; } & { bar: T; } +>foo : T + + this.withFoo().withBar().bar; +>this.withFoo().withBar().bar : T +>this.withFoo().withBar() : Builder & { foo: T; } & { bar: T; } +>this.withFoo().withBar : () => Builder & { foo: T; } & { bar: T; } +>this.withFoo() : this & { foo: T; } +>this.withFoo : () => this & { foo: T; } +>this : this +>withFoo : () => this & { foo: T; } +>withBar : () => Builder & { foo: T; } & { bar: T; } +>bar : T + + return this.withFoo().withBar(); +>this.withFoo().withBar() : Builder & { foo: T; } & { bar: T; } +>this.withFoo().withBar : () => Builder & { foo: T; } & { bar: T; } +>this.withFoo() : this & { foo: T; } +>this.withFoo : () => this & { foo: T; } +>this : this +>withFoo : () => this & { foo: T; } +>withBar : () => Builder & { foo: T; } & { bar: T; } + } +} + +declare var f: {foo: number}; +>f : { foo: number; } +>foo : number + +new Builder().withFoo().withBar().foo; +>new Builder().withFoo().withBar().foo : number +>new Builder().withFoo().withBar() : Builder & { foo: number; } & { bar: number; } +>new Builder().withFoo().withBar : () => Builder & { foo: number; } & { bar: T; } +>new Builder().withFoo() : Builder & { foo: number; } +>new Builder().withFoo : () => Builder & { foo: T; } +>new Builder() : Builder +>Builder : typeof Builder +>withFoo : () => Builder & { foo: T; } +>withBar : () => Builder & { foo: number; } & { bar: T; } +>foo : number + +new Builder().withFoo().withBar().bar; +>new Builder().withFoo().withBar().bar : number +>new Builder().withFoo().withBar() : Builder & { foo: number; } & { bar: number; } +>new Builder().withFoo().withBar : () => Builder & { foo: number; } & { bar: T; } +>new Builder().withFoo() : Builder & { foo: number; } +>new Builder().withFoo : () => Builder & { foo: T; } +>new Builder() : Builder +>Builder : typeof Builder +>withFoo : () => Builder & { foo: T; } +>withBar : () => Builder & { foo: number; } & { bar: T; } +>bar : number + +f = new Builder().withFoo().withBar(); +>f = new Builder().withFoo().withBar() : Builder & { foo: number; } & { bar: number; } +>f : { foo: number; } +>new Builder().withFoo().withBar() : Builder & { foo: number; } & { bar: number; } +>new Builder().withFoo().withBar : () => Builder & { foo: number; } & { bar: T; } +>new Builder().withFoo() : Builder & { foo: number; } +>new Builder().withFoo : () => Builder & { foo: T; } +>new Builder() : Builder +>Builder : typeof Builder +>withFoo : () => Builder & { foo: T; } +>withBar : () => Builder & { foo: number; } & { bar: T; } + +new Builder().withFooBar().foo; +>new Builder().withFooBar().foo : number +>new Builder().withFooBar() : Builder & { foo: number; } & { bar: number; } +>new Builder().withFooBar : () => Builder & { foo: T; } & { bar: T; } +>new Builder() : Builder +>Builder : typeof Builder +>withFooBar : () => Builder & { foo: T; } & { bar: T; } +>foo : number + +new Builder().withFooBar().bar; +>new Builder().withFooBar().bar : number +>new Builder().withFooBar() : Builder & { foo: number; } & { bar: number; } +>new Builder().withFooBar : () => Builder & { foo: T; } & { bar: T; } +>new Builder() : Builder +>Builder : typeof Builder +>withFooBar : () => Builder & { foo: T; } & { bar: T; } +>bar : number + +f = new Builder().withFooBar(); +>f = new Builder().withFooBar() : Builder & { foo: number; } & { bar: number; } +>f : { foo: number; } +>new Builder().withFooBar() : Builder & { foo: number; } & { bar: number; } +>new Builder().withFooBar : () => Builder & { foo: T; } & { bar: T; } +>new Builder() : Builder +>Builder : typeof Builder +>withFooBar : () => Builder & { foo: T; } & { bar: T; } + diff --git a/tests/cases/compiler/chainedThisRefinements.ts b/tests/cases/compiler/chainedThisRefinements.ts new file mode 100644 index 0000000000000..7dab042d628d2 --- /dev/null +++ b/tests/cases/compiler/chainedThisRefinements.ts @@ -0,0 +1,22 @@ +class Builder { + private _class: undefined; + withFoo() { + return this as this & { foo: T } + } + withBar() { + return this as this & { bar: T } + } + withFooBar() { + this.withFoo().withBar().foo; + this.withFoo().withBar().bar; + return this.withFoo().withBar(); + } +} + +declare var f: {foo: number}; +new Builder().withFoo().withBar().foo; +new Builder().withFoo().withBar().bar; +f = new Builder().withFoo().withBar(); +new Builder().withFooBar().foo; +new Builder().withFooBar().bar; +f = new Builder().withFooBar();