Skip to content

Commit 6f33db2

Browse files
committed
Allow looking for properties inside the base of a mixin'd class
1 parent b70f894 commit 6f33db2

9 files changed

+329
-2
lines changed

src/compiler/checker.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -20645,10 +20645,20 @@ namespace ts {
2064520645
}
2064620646
return apparentType;
2064720647
}
20648-
const prop = getPropertyOfType(apparentType, right.escapedText);
20648+
let prop = getPropertyOfType(apparentType, right.escapedText);
2064920649
if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) {
2065020650
markAliasReferenced(parentSymbol, node);
2065120651
}
20652+
20653+
// Check for properties added via mixer-style base types also
20654+
if (!prop && typeIsInterfaceType(apparentType)) {
20655+
for (const base of apparentType.resolvedBaseTypes) {
20656+
if (!prop) {
20657+
prop = getPropertyOfType(base, right.escapedText);
20658+
}
20659+
}
20660+
}
20661+
2065220662
if (!prop) {
2065320663
const indexInfo = assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined;
2065420664
if (!(indexInfo && indexInfo.type)) {

src/compiler/utilities.ts

+4
Original file line numberDiff line numberDiff line change
@@ -5440,6 +5440,10 @@ namespace ts {
54405440
? node.parent.constraint
54415441
: undefined;
54425442
}
5443+
5444+
export function typeIsInterfaceType(type: Type): type is InterfaceType {
5445+
return !!type && hasProperty(type as InterfaceType, "typeParameters") && hasProperty(type as InterfaceType, "resolvedBaseTypes");
5446+
}
54435447
}
54445448

54455449
// Simple node tests of the form `node.kind === SyntaxKind.Foo`.

src/services/services.ts

-1
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,6 @@ namespace ts {
420420
getDefault(): Type | undefined {
421421
return this.checker.getDefaultFromTypeParameter(this);
422422
}
423-
424423
isUnion(): this is UnionType {
425424
return !!(this.flags & TypeFlags.Union);
426425
}

tests/baselines/reference/api/tsserverlibrary.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3381,6 +3381,7 @@ declare namespace ts {
33813381
*/
33823382
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration>;
33833383
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
3384+
function typeIsInterfaceType(type: Type): type is InterfaceType;
33843385
}
33853386
declare namespace ts {
33863387
function isNumericLiteral(node: Node): node is NumericLiteral;

tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3381,6 +3381,7 @@ declare namespace ts {
33813381
*/
33823382
function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray<TypeParameterDeclaration>;
33833383
function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined;
3384+
function typeIsInterfaceType(type: Type): type is InterfaceType;
33843385
}
33853386
declare namespace ts {
33863387
function isNumericLiteral(node: Node): node is NumericLiteral;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//// [expressionPropertyLookupIncludesMixins.ts]
2+
// https://github.com/microsoft/TypeScript/issues/31426
3+
4+
export type AnyFunction<A = any> = (...input : any[]) => A
5+
export type AnyConstructor<A = object> = new (...input : any[]) => A
6+
export type Mixin<T extends AnyFunction> = InstanceType<ReturnType<T>>
7+
8+
export const Box = <T extends AnyConstructor<object>>(base : T) =>
9+
class Box extends base {
10+
value : any
11+
}
12+
export interface Box extends Mixin<typeof Box> {}
13+
14+
export const Observable = <T extends AnyConstructor<object>>(base : T) =>
15+
class Observable extends base {
16+
observe () : IQuark {
17+
return
18+
}
19+
}
20+
export interface Observable extends Mixin<typeof Observable> {}
21+
22+
export const CQuark = <T extends AnyConstructor<Box & Observable>>(base : T) =>
23+
class Quark extends base {
24+
25+
observe () : Quark {
26+
// No error here!
27+
this.value
28+
29+
30+
return
31+
}
32+
}
33+
export interface IQuark extends Mixin<typeof CQuark> {}
34+
35+
const test = (a : IQuark) => a.value // <-- Should not error
36+
37+
38+
//// [expressionPropertyLookupIncludesMixins.js]
39+
"use strict";
40+
// https://github.com/microsoft/TypeScript/issues/31426
41+
var __extends = (this && this.__extends) || (function () {
42+
var extendStatics = function (d, b) {
43+
extendStatics = Object.setPrototypeOf ||
44+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
45+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
46+
return extendStatics(d, b);
47+
};
48+
return function (d, b) {
49+
extendStatics(d, b);
50+
function __() { this.constructor = d; }
51+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
52+
};
53+
})();
54+
exports.__esModule = true;
55+
exports.Box = function (base) {
56+
return /** @class */ (function (_super) {
57+
__extends(Box, _super);
58+
function Box() {
59+
return _super !== null && _super.apply(this, arguments) || this;
60+
}
61+
return Box;
62+
}(base));
63+
};
64+
exports.Observable = function (base) {
65+
return /** @class */ (function (_super) {
66+
__extends(Observable, _super);
67+
function Observable() {
68+
return _super !== null && _super.apply(this, arguments) || this;
69+
}
70+
Observable.prototype.observe = function () {
71+
return;
72+
};
73+
return Observable;
74+
}(base));
75+
};
76+
exports.CQuark = function (base) {
77+
return /** @class */ (function (_super) {
78+
__extends(Quark, _super);
79+
function Quark() {
80+
return _super !== null && _super.apply(this, arguments) || this;
81+
}
82+
Quark.prototype.observe = function () {
83+
// No error here!
84+
this.value;
85+
return;
86+
};
87+
return Quark;
88+
}(base));
89+
};
90+
var test = function (a) { return a.value; }; // <-- Should not error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
=== tests/cases/compiler/expressionPropertyLookupIncludesMixins.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/31426
3+
4+
export type AnyFunction<A = any> = (...input : any[]) => A
5+
>AnyFunction : Symbol(AnyFunction, Decl(expressionPropertyLookupIncludesMixins.ts, 0, 0))
6+
>A : Symbol(A, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 24))
7+
>input : Symbol(input, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 43))
8+
>A : Symbol(A, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 24))
9+
10+
export type AnyConstructor<A = object> = new (...input : any[]) => A
11+
>AnyConstructor : Symbol(AnyConstructor, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 65))
12+
>A : Symbol(A, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 27))
13+
>input : Symbol(input, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 47))
14+
>A : Symbol(A, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 27))
15+
16+
export type Mixin<T extends AnyFunction> = InstanceType<ReturnType<T>>
17+
>Mixin : Symbol(Mixin, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 69))
18+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 4, 18))
19+
>AnyFunction : Symbol(AnyFunction, Decl(expressionPropertyLookupIncludesMixins.ts, 0, 0))
20+
>InstanceType : Symbol(InstanceType, Decl(lib.es5.d.ts, --, --))
21+
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
22+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 4, 18))
23+
24+
export const Box = <T extends AnyConstructor<object>>(base : T) =>
25+
>Box : Symbol(Box, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 9, 1))
26+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 20))
27+
>AnyConstructor : Symbol(AnyConstructor, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 65))
28+
>base : Symbol(base, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 54))
29+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 20))
30+
31+
class Box extends base {
32+
>Box : Symbol(Box, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 66))
33+
>base : Symbol(base, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 54))
34+
35+
value : any
36+
>value : Symbol(Box.value, Decl(expressionPropertyLookupIncludesMixins.ts, 7, 24))
37+
}
38+
export interface Box extends Mixin<typeof Box> {}
39+
>Box : Symbol(Box, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 9, 1))
40+
>Mixin : Symbol(Mixin, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 69))
41+
>Box : Symbol(Box, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 9, 1))
42+
43+
export const Observable = <T extends AnyConstructor<object>>(base : T) =>
44+
>Observable : Symbol(Observable, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 17, 1))
45+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 27))
46+
>AnyConstructor : Symbol(AnyConstructor, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 65))
47+
>base : Symbol(base, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 61))
48+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 27))
49+
50+
class Observable extends base {
51+
>Observable : Symbol(Observable, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 73))
52+
>base : Symbol(base, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 61))
53+
54+
observe () : IQuark {
55+
>observe : Symbol(Observable.observe, Decl(expressionPropertyLookupIncludesMixins.ts, 13, 31))
56+
>IQuark : Symbol(IQuark, Decl(expressionPropertyLookupIncludesMixins.ts, 30, 1))
57+
58+
return
59+
}
60+
}
61+
export interface Observable extends Mixin<typeof Observable> {}
62+
>Observable : Symbol(Observable, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 17, 1))
63+
>Mixin : Symbol(Mixin, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 69))
64+
>Observable : Symbol(Observable, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 17, 1))
65+
66+
export const CQuark = <T extends AnyConstructor<Box & Observable>>(base : T) =>
67+
>CQuark : Symbol(CQuark, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 12))
68+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 23))
69+
>AnyConstructor : Symbol(AnyConstructor, Decl(expressionPropertyLookupIncludesMixins.ts, 2, 65))
70+
>Box : Symbol(Box, Decl(expressionPropertyLookupIncludesMixins.ts, 6, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 9, 1))
71+
>Observable : Symbol(Observable, Decl(expressionPropertyLookupIncludesMixins.ts, 12, 12), Decl(expressionPropertyLookupIncludesMixins.ts, 17, 1))
72+
>base : Symbol(base, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 67))
73+
>T : Symbol(T, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 23))
74+
75+
class Quark extends base {
76+
>Quark : Symbol(Quark, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 79))
77+
>base : Symbol(base, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 67))
78+
79+
observe () : Quark {
80+
>observe : Symbol(Quark.observe, Decl(expressionPropertyLookupIncludesMixins.ts, 21, 26))
81+
>Quark : Symbol(Quark, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 79))
82+
83+
// No error here!
84+
this.value
85+
>this.value : Symbol(Box.value, Decl(expressionPropertyLookupIncludesMixins.ts, 7, 24))
86+
>this : Symbol(Quark, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 79))
87+
>value : Symbol(Box.value, Decl(expressionPropertyLookupIncludesMixins.ts, 7, 24))
88+
89+
90+
return
91+
}
92+
}
93+
export interface IQuark extends Mixin<typeof CQuark> {}
94+
>IQuark : Symbol(IQuark, Decl(expressionPropertyLookupIncludesMixins.ts, 30, 1))
95+
>Mixin : Symbol(Mixin, Decl(expressionPropertyLookupIncludesMixins.ts, 3, 69))
96+
>CQuark : Symbol(CQuark, Decl(expressionPropertyLookupIncludesMixins.ts, 20, 12))
97+
98+
const test = (a : IQuark) => a.value // <-- Should not error
99+
>test : Symbol(test, Decl(expressionPropertyLookupIncludesMixins.ts, 33, 5))
100+
>a : Symbol(a, Decl(expressionPropertyLookupIncludesMixins.ts, 33, 14))
101+
>IQuark : Symbol(IQuark, Decl(expressionPropertyLookupIncludesMixins.ts, 30, 1))
102+
>a.value : Symbol(Box.value, Decl(expressionPropertyLookupIncludesMixins.ts, 7, 24))
103+
>a : Symbol(a, Decl(expressionPropertyLookupIncludesMixins.ts, 33, 14))
104+
>value : Symbol(Box.value, Decl(expressionPropertyLookupIncludesMixins.ts, 7, 24))
105+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
=== tests/cases/compiler/expressionPropertyLookupIncludesMixins.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/31426
3+
4+
export type AnyFunction<A = any> = (...input : any[]) => A
5+
>AnyFunction : AnyFunction<A>
6+
>input : any[]
7+
8+
export type AnyConstructor<A = object> = new (...input : any[]) => A
9+
>AnyConstructor : AnyConstructor<A>
10+
>input : any[]
11+
12+
export type Mixin<T extends AnyFunction> = InstanceType<ReturnType<T>>
13+
>Mixin : InstanceType<ReturnType<T>>
14+
15+
export const Box = <T extends AnyConstructor<object>>(base : T) =>
16+
>Box : <T extends AnyConstructor<object>>(base: T) => { new (...input: any[]): Box; prototype: Box<any>.Box; } & T
17+
><T extends AnyConstructor<object>>(base : T) =>class Box extends base { value : any} : <T extends AnyConstructor<object>>(base: T) => { new (...input: any[]): Box; prototype: Box<any>.Box; } & T
18+
>base : T
19+
20+
class Box extends base {
21+
>class Box extends base { value : any} : { new (...input: any[]): Box; prototype: Box<any>.Box; } & T
22+
>Box : { new (...input: any[]): Box; prototype: Box<any>.Box; } & T
23+
>base : object
24+
25+
value : any
26+
>value : any
27+
}
28+
export interface Box extends Mixin<typeof Box> {}
29+
>Box : <T extends AnyConstructor<object>>(base: T) => { new (...input: any[]): Box; prototype: Box<any>.Box; } & T
30+
31+
export const Observable = <T extends AnyConstructor<object>>(base : T) =>
32+
>Observable : <T extends AnyConstructor<object>>(base: T) => { new (...input: any[]): Observable; prototype: Observable<any>.Observable; } & T
33+
><T extends AnyConstructor<object>>(base : T) =>class Observable extends base { observe () : IQuark { return }} : <T extends AnyConstructor<object>>(base: T) => { new (...input: any[]): Observable; prototype: Observable<any>.Observable; } & T
34+
>base : T
35+
36+
class Observable extends base {
37+
>class Observable extends base { observe () : IQuark { return }} : { new (...input: any[]): Observable; prototype: Observable<any>.Observable; } & T
38+
>Observable : { new (...input: any[]): Observable; prototype: Observable<any>.Observable; } & T
39+
>base : object
40+
41+
observe () : IQuark {
42+
>observe : () => IQuark
43+
44+
return
45+
}
46+
}
47+
export interface Observable extends Mixin<typeof Observable> {}
48+
>Observable : <T extends AnyConstructor<object>>(base: T) => { new (...input: any[]): Observable; prototype: Observable<any>.Observable; } & T
49+
50+
export const CQuark = <T extends AnyConstructor<Box & Observable>>(base : T) =>
51+
>CQuark : <T extends AnyConstructor<Box & Observable>>(base: T) => { new (...input: any[]): Quark; prototype: CQuark<any>.Quark; } & T
52+
><T extends AnyConstructor<Box & Observable>>(base : T) =>class Quark extends base { observe () : Quark { // No error here! this.value return }} : <T extends AnyConstructor<Box & Observable>>(base: T) => { new (...input: any[]): Quark; prototype: CQuark<any>.Quark; } & T
53+
>base : T
54+
55+
class Quark extends base {
56+
>class Quark extends base { observe () : Quark { // No error here! this.value return }} : { new (...input: any[]): Quark; prototype: CQuark<any>.Quark; } & T
57+
>Quark : { new (...input: any[]): Quark; prototype: CQuark<any>.Quark; } & T
58+
>base : Box & Observable
59+
60+
observe () : Quark {
61+
>observe : () => Quark
62+
63+
// No error here!
64+
this.value
65+
>this.value : any
66+
>this : this
67+
>value : any
68+
69+
70+
return
71+
}
72+
}
73+
export interface IQuark extends Mixin<typeof CQuark> {}
74+
>CQuark : <T extends AnyConstructor<Box & Observable>>(base: T) => { new (...input: any[]): Quark; prototype: CQuark<any>.Quark; } & T
75+
76+
const test = (a : IQuark) => a.value // <-- Should not error
77+
>test : (a: IQuark) => any
78+
>(a : IQuark) => a.value : (a: IQuark) => any
79+
>a : IQuark
80+
>a.value : any
81+
>a : IQuark
82+
>value : any
83+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// https://github.com/microsoft/TypeScript/issues/31426
2+
3+
export type AnyFunction<A = any> = (...input : any[]) => A
4+
export type AnyConstructor<A = object> = new (...input : any[]) => A
5+
export type Mixin<T extends AnyFunction> = InstanceType<ReturnType<T>>
6+
7+
export const Box = <T extends AnyConstructor<object>>(base : T) =>
8+
class Box extends base {
9+
value : any
10+
}
11+
export interface Box extends Mixin<typeof Box> {}
12+
13+
export const Observable = <T extends AnyConstructor<object>>(base : T) =>
14+
class Observable extends base {
15+
observe () : IQuark {
16+
return
17+
}
18+
}
19+
export interface Observable extends Mixin<typeof Observable> {}
20+
21+
export const CQuark = <T extends AnyConstructor<Box & Observable>>(base : T) =>
22+
class Quark extends base {
23+
24+
observe () : Quark {
25+
// No error here!
26+
this.value
27+
28+
29+
return
30+
}
31+
}
32+
export interface IQuark extends Mixin<typeof CQuark> {}
33+
34+
const test = (a : IQuark) => a.value // <-- Should not error

0 commit comments

Comments
 (0)