Skip to content

Commit eb21eb8

Browse files
authored
Ensure all default type params are mapped to some default even in circular scenarios (#28423)
* Ensure all default type params are mapped to some default even in circular scenarios * Add js example, fix typo
1 parent fe1ba9b commit eb21eb8

File tree

5 files changed

+232
-2
lines changed

5 files changed

+232
-2
lines changed

src/compiler/checker.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -7663,16 +7663,18 @@ namespace ts {
76637663
// Map an unsatisfied type parameter with a default type.
76647664
// If a type parameter does not have a default type, or if the default type
76657665
// is a forward reference, the empty object type is used.
7666+
const baseDefaultType = getDefaultTypeArgumentType(isJavaScriptImplicitAny);
7667+
const circularityMapper = createTypeMapper(typeParameters!, map(typeParameters!, () => baseDefaultType));
76667668
for (let i = numTypeArguments; i < numTypeParameters; i++) {
7667-
result[i] = getConstraintFromTypeParameter(typeParameters![i]) || getDefaultTypeArgumentType(isJavaScriptImplicitAny);
7669+
result[i] = instantiateType(getConstraintFromTypeParameter(typeParameters![i]) || baseDefaultType, circularityMapper);
76687670
}
76697671
for (let i = numTypeArguments; i < numTypeParameters; i++) {
76707672
const mapper = createTypeMapper(typeParameters!, result);
76717673
let defaultType = getDefaultFromTypeParameter(typeParameters![i]);
76727674
if (isJavaScriptImplicitAny && defaultType && isTypeIdenticalTo(defaultType, emptyObjectType)) {
76737675
defaultType = anyType;
76747676
}
7675-
result[i] = defaultType ? instantiateType(defaultType, mapper) : getDefaultTypeArgumentType(isJavaScriptImplicitAny);
7677+
result[i] = defaultType ? instantiateType(defaultType, mapper) : baseDefaultType;
76767678
}
76777679
result.length = typeParameters!.length;
76787680
return result;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [tests/cases/compiler/subclassThisTypeAssignable.ts] ////
2+
3+
//// [tile1.ts]
4+
interface Lifecycle<Attrs, State> {
5+
oninit?(vnode: Vnode<Attrs, State>): number;
6+
[_: number]: any;
7+
}
8+
9+
interface Vnode<Attrs, State extends Lifecycle<Attrs, State> = Lifecycle<Attrs, State>> {
10+
tag: Component<Attrs, State>;
11+
}
12+
13+
interface Component<Attrs, State> {
14+
view(this: State, vnode: Vnode<Attrs, State>): number;
15+
}
16+
17+
interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
18+
oninit?(vnode: Vnode<A, this>): number;
19+
view(vnode: Vnode<A, this>): number;
20+
}
21+
22+
interface MyAttrs { id: number }
23+
class C implements ClassComponent<MyAttrs> {
24+
view(v: Vnode<MyAttrs>) { return 0; }
25+
}
26+
27+
const test8: ClassComponent<any> = new C();
28+
//// [file1.js]
29+
/** @type {ClassComponent<any>} */
30+
const test9 = new C();
31+
32+
33+
//// [tile1.js]
34+
var C = /** @class */ (function () {
35+
function C() {
36+
}
37+
C.prototype.view = function (v) { return 0; };
38+
return C;
39+
}());
40+
var test8 = new C();
41+
//// [file1.js]
42+
/** @type {ClassComponent<any>} */
43+
var test9 = new C();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
=== tests/cases/compiler/tile1.ts ===
2+
interface Lifecycle<Attrs, State> {
3+
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
4+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 0, 20))
5+
>State : Symbol(State, Decl(tile1.ts, 0, 26))
6+
7+
oninit?(vnode: Vnode<Attrs, State>): number;
8+
>oninit : Symbol(Lifecycle.oninit, Decl(tile1.ts, 0, 35))
9+
>vnode : Symbol(vnode, Decl(tile1.ts, 1, 9))
10+
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
11+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 0, 20))
12+
>State : Symbol(State, Decl(tile1.ts, 0, 26))
13+
14+
[_: number]: any;
15+
>_ : Symbol(_, Decl(tile1.ts, 2, 2))
16+
}
17+
18+
interface Vnode<Attrs, State extends Lifecycle<Attrs, State> = Lifecycle<Attrs, State>> {
19+
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
20+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
21+
>State : Symbol(State, Decl(tile1.ts, 5, 22))
22+
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
23+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
24+
>State : Symbol(State, Decl(tile1.ts, 5, 22))
25+
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
26+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
27+
>State : Symbol(State, Decl(tile1.ts, 5, 22))
28+
29+
tag: Component<Attrs, State>;
30+
>tag : Symbol(Vnode.tag, Decl(tile1.ts, 5, 89))
31+
>Component : Symbol(Component, Decl(tile1.ts, 7, 1))
32+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 5, 16))
33+
>State : Symbol(State, Decl(tile1.ts, 5, 22))
34+
}
35+
36+
interface Component<Attrs, State> {
37+
>Component : Symbol(Component, Decl(tile1.ts, 7, 1))
38+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 9, 20))
39+
>State : Symbol(State, Decl(tile1.ts, 9, 26))
40+
41+
view(this: State, vnode: Vnode<Attrs, State>): number;
42+
>view : Symbol(Component.view, Decl(tile1.ts, 9, 35))
43+
>this : Symbol(this, Decl(tile1.ts, 10, 6))
44+
>State : Symbol(State, Decl(tile1.ts, 9, 26))
45+
>vnode : Symbol(vnode, Decl(tile1.ts, 10, 18))
46+
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
47+
>Attrs : Symbol(Attrs, Decl(tile1.ts, 9, 20))
48+
>State : Symbol(State, Decl(tile1.ts, 9, 26))
49+
}
50+
51+
interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
52+
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
53+
>A : Symbol(A, Decl(tile1.ts, 13, 25))
54+
>Lifecycle : Symbol(Lifecycle, Decl(tile1.ts, 0, 0))
55+
>A : Symbol(A, Decl(tile1.ts, 13, 25))
56+
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
57+
>A : Symbol(A, Decl(tile1.ts, 13, 25))
58+
59+
oninit?(vnode: Vnode<A, this>): number;
60+
>oninit : Symbol(ClassComponent.oninit, Decl(tile1.ts, 13, 69))
61+
>vnode : Symbol(vnode, Decl(tile1.ts, 14, 9))
62+
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
63+
>A : Symbol(A, Decl(tile1.ts, 13, 25))
64+
65+
view(vnode: Vnode<A, this>): number;
66+
>view : Symbol(ClassComponent.view, Decl(tile1.ts, 14, 40))
67+
>vnode : Symbol(vnode, Decl(tile1.ts, 15, 6))
68+
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
69+
>A : Symbol(A, Decl(tile1.ts, 13, 25))
70+
}
71+
72+
interface MyAttrs { id: number }
73+
>MyAttrs : Symbol(MyAttrs, Decl(tile1.ts, 16, 1))
74+
>id : Symbol(MyAttrs.id, Decl(tile1.ts, 18, 19))
75+
76+
class C implements ClassComponent<MyAttrs> {
77+
>C : Symbol(C, Decl(tile1.ts, 18, 32))
78+
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
79+
>MyAttrs : Symbol(MyAttrs, Decl(tile1.ts, 16, 1))
80+
81+
view(v: Vnode<MyAttrs>) { return 0; }
82+
>view : Symbol(C.view, Decl(tile1.ts, 19, 44))
83+
>v : Symbol(v, Decl(tile1.ts, 20, 6))
84+
>Vnode : Symbol(Vnode, Decl(tile1.ts, 3, 1))
85+
>MyAttrs : Symbol(MyAttrs, Decl(tile1.ts, 16, 1))
86+
}
87+
88+
const test8: ClassComponent<any> = new C();
89+
>test8 : Symbol(test8, Decl(tile1.ts, 23, 5))
90+
>ClassComponent : Symbol(ClassComponent, Decl(tile1.ts, 11, 1))
91+
>C : Symbol(C, Decl(tile1.ts, 18, 32))
92+
93+
=== tests/cases/compiler/file1.js ===
94+
/** @type {ClassComponent<any>} */
95+
const test9 = new C();
96+
>test9 : Symbol(test9, Decl(file1.js, 1, 5))
97+
>C : Symbol(C, Decl(tile1.ts, 18, 32))
98+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
=== tests/cases/compiler/tile1.ts ===
2+
interface Lifecycle<Attrs, State> {
3+
oninit?(vnode: Vnode<Attrs, State>): number;
4+
>oninit : (vnode: Vnode<Attrs, State>) => number
5+
>vnode : Vnode<Attrs, State>
6+
7+
[_: number]: any;
8+
>_ : number
9+
}
10+
11+
interface Vnode<Attrs, State extends Lifecycle<Attrs, State> = Lifecycle<Attrs, State>> {
12+
tag: Component<Attrs, State>;
13+
>tag : Component<Attrs, State>
14+
}
15+
16+
interface Component<Attrs, State> {
17+
view(this: State, vnode: Vnode<Attrs, State>): number;
18+
>view : (this: State, vnode: Vnode<Attrs, State>) => number
19+
>this : State
20+
>vnode : Vnode<Attrs, State>
21+
}
22+
23+
interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
24+
oninit?(vnode: Vnode<A, this>): number;
25+
>oninit : (vnode: Vnode<A, this>) => number
26+
>vnode : Vnode<A, this>
27+
28+
view(vnode: Vnode<A, this>): number;
29+
>view : (vnode: Vnode<A, this>) => number
30+
>vnode : Vnode<A, this>
31+
}
32+
33+
interface MyAttrs { id: number }
34+
>id : number
35+
36+
class C implements ClassComponent<MyAttrs> {
37+
>C : C
38+
39+
view(v: Vnode<MyAttrs>) { return 0; }
40+
>view : (v: Vnode<MyAttrs, Lifecycle<MyAttrs, Lifecycle<{}, {}>>>) => number
41+
>v : Vnode<MyAttrs, Lifecycle<MyAttrs, Lifecycle<{}, {}>>>
42+
>0 : 0
43+
}
44+
45+
const test8: ClassComponent<any> = new C();
46+
>test8 : ClassComponent<any>
47+
>new C() : C
48+
>C : typeof C
49+
50+
=== tests/cases/compiler/file1.js ===
51+
/** @type {ClassComponent<any>} */
52+
const test9 = new C();
53+
>test9 : ClassComponent<any>
54+
>new C() : C
55+
>C : typeof C
56+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @outDir: ./out
4+
// @filename: tile1.ts
5+
interface Lifecycle<Attrs, State> {
6+
oninit?(vnode: Vnode<Attrs, State>): number;
7+
[_: number]: any;
8+
}
9+
10+
interface Vnode<Attrs, State extends Lifecycle<Attrs, State> = Lifecycle<Attrs, State>> {
11+
tag: Component<Attrs, State>;
12+
}
13+
14+
interface Component<Attrs, State> {
15+
view(this: State, vnode: Vnode<Attrs, State>): number;
16+
}
17+
18+
interface ClassComponent<A> extends Lifecycle<A, ClassComponent<A>> {
19+
oninit?(vnode: Vnode<A, this>): number;
20+
view(vnode: Vnode<A, this>): number;
21+
}
22+
23+
interface MyAttrs { id: number }
24+
class C implements ClassComponent<MyAttrs> {
25+
view(v: Vnode<MyAttrs>) { return 0; }
26+
}
27+
28+
const test8: ClassComponent<any> = new C();
29+
// @filename: file1.js
30+
/** @type {ClassComponent<any>} */
31+
const test9 = new C();

0 commit comments

Comments
 (0)