Skip to content

Commit c35763c

Browse files
committed
Quick and dirty workaround
1 parent 2cc4f53 commit c35763c

5 files changed

+211
-0
lines changed

src/compiler/checker.ts

+12
Original file line numberDiff line numberDiff line change
@@ -11378,6 +11378,18 @@ namespace ts {
1137811378
}
1137911379

1138011380
function getTypeWithFacts(type: Type, include: TypeFacts) {
11381+
if (type.flags & TypeFlags.IndexedAccess) {
11382+
// TODO (weswig): This is a substitute for a lazy negated type to remove the types indicated by the TypeFacts from the (potential) union the IndexedAccess refers to
11383+
// - instead of defering the resolution of the access, we apply the facts to the base constraint of the access instead; so this works under most circumstances,
11384+
// like when the index refers to an optional member and you want to access that member, but unfortunately if that member's type is supposed to vary with the base,
11385+
// then the eager evaluation of this removes that relationship
11386+
const innerType = getBaseConstraintOfType(type) || emptyObjectType;
11387+
const result = filterType(innerType, t => (getTypeFacts(t) & include) !== 0);
11388+
if (result !== innerType) {
11389+
return result;
11390+
}
11391+
return type;
11392+
}
1138111393
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
1138211394
}
1138311395

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//// [strictNullNotNullIndexTypeShouldWork.ts]
2+
interface A {
3+
params?: { name: string; };
4+
}
5+
6+
class Test<T extends A> {
7+
attrs: Readonly<T>;
8+
9+
m() {
10+
this.attrs.params!.name;
11+
}
12+
}
13+
14+
interface Foo {
15+
foo?: number;
16+
}
17+
18+
class FooClass<P extends Foo = Foo> {
19+
properties: Readonly<P>;
20+
21+
foo(): number {
22+
const { foo = 42 } = this.properties;
23+
return foo;
24+
}
25+
}
26+
27+
//// [strictNullNotNullIndexTypeShouldWork.js]
28+
var Test = /** @class */ (function () {
29+
function Test() {
30+
}
31+
Test.prototype.m = function () {
32+
this.attrs.params.name;
33+
};
34+
return Test;
35+
}());
36+
var FooClass = /** @class */ (function () {
37+
function FooClass() {
38+
}
39+
FooClass.prototype.foo = function () {
40+
var _a = this.properties.foo, foo = _a === void 0 ? 42 : _a;
41+
return foo;
42+
};
43+
return FooClass;
44+
}());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
=== tests/cases/compiler/strictNullNotNullIndexTypeShouldWork.ts ===
2+
interface A {
3+
>A : Symbol(A, Decl(strictNullNotNullIndexTypeShouldWork.ts, 0, 0))
4+
5+
params?: { name: string; };
6+
>params : Symbol(A.params, Decl(strictNullNotNullIndexTypeShouldWork.ts, 0, 13))
7+
>name : Symbol(name, Decl(strictNullNotNullIndexTypeShouldWork.ts, 1, 14))
8+
}
9+
10+
class Test<T extends A> {
11+
>Test : Symbol(Test, Decl(strictNullNotNullIndexTypeShouldWork.ts, 2, 1))
12+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeShouldWork.ts, 4, 11))
13+
>A : Symbol(A, Decl(strictNullNotNullIndexTypeShouldWork.ts, 0, 0))
14+
15+
attrs: Readonly<T>;
16+
>attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeShouldWork.ts, 4, 25))
17+
>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --))
18+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeShouldWork.ts, 4, 11))
19+
20+
m() {
21+
>m : Symbol(Test.m, Decl(strictNullNotNullIndexTypeShouldWork.ts, 5, 23))
22+
23+
this.attrs.params!.name;
24+
>this.attrs.params!.name : Symbol(name, Decl(strictNullNotNullIndexTypeShouldWork.ts, 1, 14))
25+
>this.attrs.params : Symbol(params, Decl(strictNullNotNullIndexTypeShouldWork.ts, 0, 13))
26+
>this.attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeShouldWork.ts, 4, 25))
27+
>this : Symbol(Test, Decl(strictNullNotNullIndexTypeShouldWork.ts, 2, 1))
28+
>attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeShouldWork.ts, 4, 25))
29+
>params : Symbol(params, Decl(strictNullNotNullIndexTypeShouldWork.ts, 0, 13))
30+
>name : Symbol(name, Decl(strictNullNotNullIndexTypeShouldWork.ts, 1, 14))
31+
}
32+
}
33+
34+
interface Foo {
35+
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 10, 1))
36+
37+
foo?: number;
38+
>foo : Symbol(Foo.foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 12, 15))
39+
}
40+
41+
class FooClass<P extends Foo = Foo> {
42+
>FooClass : Symbol(FooClass, Decl(strictNullNotNullIndexTypeShouldWork.ts, 14, 1))
43+
>P : Symbol(P, Decl(strictNullNotNullIndexTypeShouldWork.ts, 16, 15))
44+
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 10, 1))
45+
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 10, 1))
46+
47+
properties: Readonly<P>;
48+
>properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeShouldWork.ts, 16, 37))
49+
>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --))
50+
>P : Symbol(P, Decl(strictNullNotNullIndexTypeShouldWork.ts, 16, 15))
51+
52+
foo(): number {
53+
>foo : Symbol(FooClass.foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 17, 28))
54+
55+
const { foo = 42 } = this.properties;
56+
>foo : Symbol(foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 20, 15))
57+
>this.properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeShouldWork.ts, 16, 37))
58+
>this : Symbol(FooClass, Decl(strictNullNotNullIndexTypeShouldWork.ts, 14, 1))
59+
>properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeShouldWork.ts, 16, 37))
60+
61+
return foo;
62+
>foo : Symbol(foo, Decl(strictNullNotNullIndexTypeShouldWork.ts, 20, 15))
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
=== tests/cases/compiler/strictNullNotNullIndexTypeShouldWork.ts ===
2+
interface A {
3+
>A : A
4+
5+
params?: { name: string; };
6+
>params : { name: string; } | undefined
7+
>name : string
8+
}
9+
10+
class Test<T extends A> {
11+
>Test : Test<T>
12+
>T : T
13+
>A : A
14+
15+
attrs: Readonly<T>;
16+
>attrs : Readonly<T>
17+
>Readonly : Readonly<T>
18+
>T : T
19+
20+
m() {
21+
>m : () => void
22+
23+
this.attrs.params!.name;
24+
>this.attrs.params!.name : string
25+
>this.attrs.params! : { name: string; }
26+
>this.attrs.params : T["params"]
27+
>this.attrs : Readonly<T>
28+
>this : this
29+
>attrs : Readonly<T>
30+
>params : T["params"]
31+
>name : string
32+
}
33+
}
34+
35+
interface Foo {
36+
>Foo : Foo
37+
38+
foo?: number;
39+
>foo : number | undefined
40+
}
41+
42+
class FooClass<P extends Foo = Foo> {
43+
>FooClass : FooClass<P>
44+
>P : P
45+
>Foo : Foo
46+
>Foo : Foo
47+
48+
properties: Readonly<P>;
49+
>properties : Readonly<P>
50+
>Readonly : Readonly<T>
51+
>P : P
52+
53+
foo(): number {
54+
>foo : () => number
55+
56+
const { foo = 42 } = this.properties;
57+
>foo : number
58+
>42 : 42
59+
>this.properties : Readonly<P>
60+
>this : this
61+
>properties : Readonly<P>
62+
63+
return foo;
64+
>foo : number
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strictNullChecks: true
2+
interface A {
3+
params?: { name: string; };
4+
}
5+
6+
class Test<T extends A> {
7+
attrs: Readonly<T>;
8+
9+
m() {
10+
this.attrs.params!.name;
11+
}
12+
}
13+
14+
interface Foo {
15+
foo?: number;
16+
}
17+
18+
class FooClass<P extends Foo = Foo> {
19+
properties: Readonly<P>;
20+
21+
foo(): number {
22+
const { foo = 42 } = this.properties;
23+
return foo;
24+
}
25+
}

0 commit comments

Comments
 (0)