Skip to content

Instantiate this in non-super property/element access expressions #29468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6631,6 +6631,9 @@ namespace ts {
else if (type.flags & TypeFlags.Intersection) {
return getIntersectionType(map((<IntersectionType>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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. can you format the conditional one more than one line? It's not readable as-is. (I would extract the middle argument to a temp.)
  2. Why create a reference to globalThisType?

}
return needApparentType ? getApparentType(type) : type;
}

Expand Down Expand Up @@ -6685,6 +6688,23 @@ namespace ts {
}

function resolveTypeReferenceMembers(type: TypeReference): void {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra newline is extra

if (type.target === globalThisType && type.typeArguments && type.typeArguments[0].flags & TypeFlags.TypeParameter &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't understand what globalThisType is doing here (unless I'm completely mistaken and it's a marker type rather than the type of globalThis.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the ThisType<T> marker type.

(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 ?
Expand Down
48 changes: 48 additions & 0 deletions tests/baselines/reference/chainedThisRefinements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//// [chainedThisRefinements.ts]
class Builder {
private _class: undefined;
withFoo<T>() {
return this as this & { foo: T }
}
withBar<T>() {
return this as this & { bar: T }
}
withFooBar<T>() {
this.withFoo<T>().withBar<T>().foo;
this.withFoo<T>().withBar<T>().bar;
return this.withFoo<T>().withBar<T>();
}
}

declare var f: {foo: number};
new Builder().withFoo<number>().withBar<number>().foo;
new Builder().withFoo<number>().withBar<number>().bar;
f = new Builder().withFoo<number>().withBar<number>();
new Builder().withFooBar<number>().foo;
new Builder().withFooBar<number>().bar;
f = new Builder().withFooBar<number>();


//// [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();
112 changes: 112 additions & 0 deletions tests/baselines/reference/chainedThisRefinements.symbols
Original file line number Diff line number Diff line change
@@ -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<T>() {
>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<T>() {
>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<T>() {
>withFooBar : Symbol(Builder.withFooBar, Decl(chainedThisRefinements.ts, 7, 5))
>T : Symbol(T, Decl(chainedThisRefinements.ts, 8, 15))

this.withFoo<T>().withBar<T>().foo;
>this.withFoo<T>().withBar<T>().foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31))
>this.withFoo<T>().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<T>().withBar<T>().bar;
>this.withFoo<T>().withBar<T>().bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31))
>this.withFoo<T>().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<T>().withBar<T>();
>this.withFoo<T>().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<number>().withBar<number>().foo;
>new Builder().withFoo<number>().withBar<number>().foo : Symbol(foo, Decl(chainedThisRefinements.ts, 3, 31))
>new Builder().withFoo<number>().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<number>().withBar<number>().bar;
>new Builder().withFoo<number>().withBar<number>().bar : Symbol(bar, Decl(chainedThisRefinements.ts, 6, 31))
>new Builder().withFoo<number>().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<number>().withBar<number>();
>f : Symbol(f, Decl(chainedThisRefinements.ts, 15, 11))
>new Builder().withFoo<number>().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<number>().foo;
>new Builder().withFooBar<number>().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<number>().bar;
>new Builder().withFooBar<number>().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<number>();
>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))

126 changes: 126 additions & 0 deletions tests/baselines/reference/chainedThisRefinements.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
=== tests/cases/compiler/chainedThisRefinements.ts ===
class Builder {
>Builder : Builder

private _class: undefined;
>_class : undefined

withFoo<T>() {
>withFoo : <T>() => this & { foo: T; }

return this as this & { foo: T }
>this as this & { foo: T } : this & { foo: T; }
>this : this
>foo : T
}
withBar<T>() {
>withBar : <T>() => this & { bar: T; }

return this as this & { bar: T }
>this as this & { bar: T } : this & { bar: T; }
>this : this
>bar : T
}
withFooBar<T>() {
>withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }

this.withFoo<T>().withBar<T>().foo;
>this.withFoo<T>().withBar<T>().foo : T
>this.withFoo<T>().withBar<T>() : Builder & { foo: T; } & { bar: T; }
>this.withFoo<T>().withBar : <T>() => Builder & { foo: T; } & { bar: T; }
>this.withFoo<T>() : this & { foo: T; }
>this.withFoo : <T>() => this & { foo: T; }
>this : this
>withFoo : <T>() => this & { foo: T; }
>withBar : <T>() => Builder & { foo: T; } & { bar: T; }
>foo : T

this.withFoo<T>().withBar<T>().bar;
>this.withFoo<T>().withBar<T>().bar : T
>this.withFoo<T>().withBar<T>() : Builder & { foo: T; } & { bar: T; }
>this.withFoo<T>().withBar : <T>() => Builder & { foo: T; } & { bar: T; }
>this.withFoo<T>() : this & { foo: T; }
>this.withFoo : <T>() => this & { foo: T; }
>this : this
>withFoo : <T>() => this & { foo: T; }
>withBar : <T>() => Builder & { foo: T; } & { bar: T; }
>bar : T

return this.withFoo<T>().withBar<T>();
>this.withFoo<T>().withBar<T>() : Builder & { foo: T; } & { bar: T; }
>this.withFoo<T>().withBar : <T>() => Builder & { foo: T; } & { bar: T; }
>this.withFoo<T>() : this & { foo: T; }
>this.withFoo : <T>() => this & { foo: T; }
>this : this
>withFoo : <T>() => this & { foo: T; }
>withBar : <T>() => Builder & { foo: T; } & { bar: T; }
}
}

declare var f: {foo: number};
>f : { foo: number; }
>foo : number

new Builder().withFoo<number>().withBar<number>().foo;
>new Builder().withFoo<number>().withBar<number>().foo : number
>new Builder().withFoo<number>().withBar<number>() : Builder & { foo: number; } & { bar: number; }
>new Builder().withFoo<number>().withBar : <T>() => Builder & { foo: number; } & { bar: T; }
>new Builder().withFoo<number>() : Builder & { foo: number; }
>new Builder().withFoo : <T>() => Builder & { foo: T; }
>new Builder() : Builder
>Builder : typeof Builder
>withFoo : <T>() => Builder & { foo: T; }
>withBar : <T>() => Builder & { foo: number; } & { bar: T; }
>foo : number

new Builder().withFoo<number>().withBar<number>().bar;
>new Builder().withFoo<number>().withBar<number>().bar : number
>new Builder().withFoo<number>().withBar<number>() : Builder & { foo: number; } & { bar: number; }
>new Builder().withFoo<number>().withBar : <T>() => Builder & { foo: number; } & { bar: T; }
>new Builder().withFoo<number>() : Builder & { foo: number; }
>new Builder().withFoo : <T>() => Builder & { foo: T; }
>new Builder() : Builder
>Builder : typeof Builder
>withFoo : <T>() => Builder & { foo: T; }
>withBar : <T>() => Builder & { foo: number; } & { bar: T; }
>bar : number

f = new Builder().withFoo<number>().withBar<number>();
>f = new Builder().withFoo<number>().withBar<number>() : Builder & { foo: number; } & { bar: number; }
>f : { foo: number; }
>new Builder().withFoo<number>().withBar<number>() : Builder & { foo: number; } & { bar: number; }
>new Builder().withFoo<number>().withBar : <T>() => Builder & { foo: number; } & { bar: T; }
>new Builder().withFoo<number>() : Builder & { foo: number; }
>new Builder().withFoo : <T>() => Builder & { foo: T; }
>new Builder() : Builder
>Builder : typeof Builder
>withFoo : <T>() => Builder & { foo: T; }
>withBar : <T>() => Builder & { foo: number; } & { bar: T; }

new Builder().withFooBar<number>().foo;
>new Builder().withFooBar<number>().foo : number
>new Builder().withFooBar<number>() : Builder & { foo: number; } & { bar: number; }
>new Builder().withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }
>new Builder() : Builder
>Builder : typeof Builder
>withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }
>foo : number

new Builder().withFooBar<number>().bar;
>new Builder().withFooBar<number>().bar : number
>new Builder().withFooBar<number>() : Builder & { foo: number; } & { bar: number; }
>new Builder().withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }
>new Builder() : Builder
>Builder : typeof Builder
>withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }
>bar : number

f = new Builder().withFooBar<number>();
>f = new Builder().withFooBar<number>() : Builder & { foo: number; } & { bar: number; }
>f : { foo: number; }
>new Builder().withFooBar<number>() : Builder & { foo: number; } & { bar: number; }
>new Builder().withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }
>new Builder() : Builder
>Builder : typeof Builder
>withFooBar : <T>() => Builder & { foo: T; } & { bar: T; }

22 changes: 22 additions & 0 deletions tests/cases/compiler/chainedThisRefinements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Builder {
private _class: undefined;
withFoo<T>() {
return this as this & { foo: T }
}
withBar<T>() {
return this as this & { bar: T }
}
withFooBar<T>() {
this.withFoo<T>().withBar<T>().foo;
this.withFoo<T>().withBar<T>().bar;
return this.withFoo<T>().withBar<T>();
}
}

declare var f: {foo: number};
new Builder().withFoo<number>().withBar<number>().foo;
new Builder().withFoo<number>().withBar<number>().bar;
f = new Builder().withFoo<number>().withBar<number>();
new Builder().withFooBar<number>().foo;
new Builder().withFooBar<number>().bar;
f = new Builder().withFooBar<number>();