Skip to content

Commit 2f0a13c

Browse files
authored
Use global NonNullable type for nonnull types (#22096)
* Use NonNullable type for nonnull types * Add noLib test * Remove conditional type fallback for when lib is not present
1 parent 4ddf045 commit 2f0a13c

8 files changed

+365
-14
lines changed

src/compiler/checker.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ namespace ts {
401401
let anyArrayType: Type;
402402
let autoArrayType: Type;
403403
let anyReadonlyArrayType: Type;
404+
let deferredGlobalNonNullableTypeAlias: Symbol;
404405

405406
// The library files are only loaded when the feature is used.
406407
// This allows users to just specify library files they want to used through --lib
@@ -11122,8 +11123,19 @@ namespace ts {
1112211123
return type.flags & TypeFlags.Undefined ? type : getUnionType([type, undefinedType]);
1112311124
}
1112411125

11126+
function getGlobalNonNullableTypeInstantiation(type: Type) {
11127+
if (!deferredGlobalNonNullableTypeAlias) {
11128+
deferredGlobalNonNullableTypeAlias = getGlobalSymbol("NonNullable" as __String, SymbolFlags.TypeAlias, /*diagnostic*/ undefined) || unknownSymbol;
11129+
}
11130+
// Use NonNullable global type alias if available to improve quick info/declaration emit
11131+
if (deferredGlobalNonNullableTypeAlias !== unknownSymbol) {
11132+
return getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [type]);
11133+
}
11134+
return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); // Type alias unavailable, fall back to non-higherorder behavior
11135+
}
11136+
1112511137
function getNonNullableType(type: Type): Type {
11126-
return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
11138+
return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type;
1112711139
}
1112811140

1112911141
/**
@@ -13427,7 +13439,6 @@ namespace ts {
1342713439
return parent.kind === SyntaxKind.PropertyAccessExpression ||
1342813440
parent.kind === SyntaxKind.CallExpression && (<CallExpression>parent).expression === node ||
1342913441
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node ||
13430-
parent.kind === SyntaxKind.NonNullExpression ||
1343113442
parent.kind === SyntaxKind.BindingElement && (<BindingElement>parent).name === node && !!(<BindingElement>parent).initializer;
1343213443
}
1343313444

tests/baselines/reference/nonNullParameterExtendingStringAssignableToString.types

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ function fn<T extends string | undefined, U extends string>(one: T, two: U) {
2323
foo(one!);
2424
>foo(one!) : void
2525
>foo : (p: string) => void
26-
>one! : string
27-
>one : string | undefined
26+
>one! : NonNullable<T>
27+
>one : T
2828

2929
foo(two!);
3030
>foo(two!) : void
3131
>foo : (p: string) => void
32-
>two! : U
32+
>two! : NonNullable<U>
3333
>two : U
3434

3535
foo(three!); // this line is the important one
3636
>foo(three!) : void
3737
>foo : (p: string) => void
38-
>three! : string
39-
>three : string
38+
>three! : NonNullable<T> | NonNullable<U>
39+
>three : T | U
4040
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
error TS2318: Cannot find global type 'Array'.
2+
error TS2318: Cannot find global type 'Boolean'.
3+
error TS2318: Cannot find global type 'Function'.
4+
error TS2318: Cannot find global type 'IArguments'.
5+
error TS2318: Cannot find global type 'Number'.
6+
error TS2318: Cannot find global type 'Object'.
7+
error TS2318: Cannot find global type 'RegExp'.
8+
error TS2318: Cannot find global type 'String'.
9+
tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts(10,28): error TS2339: Property 'name' does not exist on type 'T["params"]'.
10+
11+
12+
!!! error TS2318: Cannot find global type 'Array'.
13+
!!! error TS2318: Cannot find global type 'Boolean'.
14+
!!! error TS2318: Cannot find global type 'Function'.
15+
!!! error TS2318: Cannot find global type 'IArguments'.
16+
!!! error TS2318: Cannot find global type 'Number'.
17+
!!! error TS2318: Cannot find global type 'Object'.
18+
!!! error TS2318: Cannot find global type 'RegExp'.
19+
!!! error TS2318: Cannot find global type 'String'.
20+
==== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts (1 errors) ====
21+
type Readonly<T> = {readonly [K in keyof T]: T[K]}
22+
interface A {
23+
params?: { name: string; };
24+
}
25+
26+
class Test<T extends A> {
27+
attrs: Readonly<T>;
28+
29+
m() {
30+
this.attrs.params!.name;
31+
~~~~
32+
!!! error TS2339: Property 'name' does not exist on type 'T["params"]'.
33+
}
34+
}
35+
36+
interface Foo {
37+
foo?: number;
38+
}
39+
40+
class FooClass<P extends Foo = Foo> {
41+
properties: Readonly<P>;
42+
43+
foo(): number {
44+
const { foo = 42 } = this.properties;
45+
return foo;
46+
}
47+
}
48+
49+
class Test2<T extends A> {
50+
attrs: Readonly<T>;
51+
52+
m() {
53+
return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//// [strictNullNotNullIndexTypeNoLib.ts]
2+
type Readonly<T> = {readonly [K in keyof T]: T[K]}
3+
interface A {
4+
params?: { name: string; };
5+
}
6+
7+
class Test<T extends A> {
8+
attrs: Readonly<T>;
9+
10+
m() {
11+
this.attrs.params!.name;
12+
}
13+
}
14+
15+
interface Foo {
16+
foo?: number;
17+
}
18+
19+
class FooClass<P extends Foo = Foo> {
20+
properties: Readonly<P>;
21+
22+
foo(): number {
23+
const { foo = 42 } = this.properties;
24+
return foo;
25+
}
26+
}
27+
28+
class Test2<T extends A> {
29+
attrs: Readonly<T>;
30+
31+
m() {
32+
return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
33+
}
34+
}
35+
36+
//// [strictNullNotNullIndexTypeNoLib.js]
37+
var Test = /** @class */ (function () {
38+
function Test() {
39+
}
40+
Test.prototype.m = function () {
41+
this.attrs.params.name;
42+
};
43+
return Test;
44+
}());
45+
var FooClass = /** @class */ (function () {
46+
function FooClass() {
47+
}
48+
FooClass.prototype.foo = function () {
49+
var _a = this.properties.foo, foo = _a === void 0 ? 42 : _a;
50+
return foo;
51+
};
52+
return FooClass;
53+
}());
54+
var Test2 = /** @class */ (function () {
55+
function Test2() {
56+
}
57+
Test2.prototype.m = function () {
58+
return this.attrs.params; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
59+
};
60+
return Test2;
61+
}());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
=== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts ===
2+
type Readonly<T> = {readonly [K in keyof T]: T[K]}
3+
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
4+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 14))
5+
>K : Symbol(K, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 30))
6+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 14))
7+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 14))
8+
>K : Symbol(K, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 30))
9+
10+
interface A {
11+
>A : Symbol(A, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 50))
12+
13+
params?: { name: string; };
14+
>params : Symbol(A.params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
15+
>name : Symbol(name, Decl(strictNullNotNullIndexTypeNoLib.ts, 2, 14))
16+
}
17+
18+
class Test<T extends A> {
19+
>Test : Symbol(Test, Decl(strictNullNotNullIndexTypeNoLib.ts, 3, 1))
20+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 11))
21+
>A : Symbol(A, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 50))
22+
23+
attrs: Readonly<T>;
24+
>attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25))
25+
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
26+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 11))
27+
28+
m() {
29+
>m : Symbol(Test.m, Decl(strictNullNotNullIndexTypeNoLib.ts, 6, 23))
30+
31+
this.attrs.params!.name;
32+
>this.attrs.params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
33+
>this.attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25))
34+
>this : Symbol(Test, Decl(strictNullNotNullIndexTypeNoLib.ts, 3, 1))
35+
>attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25))
36+
>params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
37+
}
38+
}
39+
40+
interface Foo {
41+
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 11, 1))
42+
43+
foo?: number;
44+
>foo : Symbol(Foo.foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 13, 15))
45+
}
46+
47+
class FooClass<P extends Foo = Foo> {
48+
>FooClass : Symbol(FooClass, Decl(strictNullNotNullIndexTypeNoLib.ts, 15, 1))
49+
>P : Symbol(P, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 15))
50+
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 11, 1))
51+
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 11, 1))
52+
53+
properties: Readonly<P>;
54+
>properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 37))
55+
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
56+
>P : Symbol(P, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 15))
57+
58+
foo(): number {
59+
>foo : Symbol(FooClass.foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 18, 28))
60+
61+
const { foo = 42 } = this.properties;
62+
>foo : Symbol(foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 21, 15))
63+
>this.properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 37))
64+
>this : Symbol(FooClass, Decl(strictNullNotNullIndexTypeNoLib.ts, 15, 1))
65+
>properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 37))
66+
67+
return foo;
68+
>foo : Symbol(foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 21, 15))
69+
}
70+
}
71+
72+
class Test2<T extends A> {
73+
>Test2 : Symbol(Test2, Decl(strictNullNotNullIndexTypeNoLib.ts, 24, 1))
74+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 12))
75+
>A : Symbol(A, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 50))
76+
77+
attrs: Readonly<T>;
78+
>attrs : Symbol(Test2.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 26))
79+
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
80+
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 12))
81+
82+
m() {
83+
>m : Symbol(Test2.m, Decl(strictNullNotNullIndexTypeNoLib.ts, 27, 23))
84+
85+
return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
86+
>this.attrs.params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
87+
>this.attrs : Symbol(Test2.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 26))
88+
>this : Symbol(Test2, Decl(strictNullNotNullIndexTypeNoLib.ts, 24, 1))
89+
>attrs : Symbol(Test2.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 26))
90+
>params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
=== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts ===
2+
type Readonly<T> = {readonly [K in keyof T]: T[K]}
3+
>Readonly : Readonly<T>
4+
>T : T
5+
>K : K
6+
>T : T
7+
>T : T
8+
>K : K
9+
10+
interface A {
11+
>A : A
12+
13+
params?: { name: string; };
14+
>params : { name: string; } | undefined
15+
>name : string
16+
}
17+
18+
class Test<T extends A> {
19+
>Test : Test<T>
20+
>T : T
21+
>A : A
22+
23+
attrs: Readonly<T>;
24+
>attrs : Readonly<T>
25+
>Readonly : Readonly<T>
26+
>T : T
27+
28+
m() {
29+
>m : () => void
30+
31+
this.attrs.params!.name;
32+
>this.attrs.params!.name : any
33+
>this.attrs.params! : T["params"]
34+
>this.attrs.params : T["params"]
35+
>this.attrs : Readonly<T>
36+
>this : this
37+
>attrs : Readonly<T>
38+
>params : T["params"]
39+
>name : any
40+
}
41+
}
42+
43+
interface Foo {
44+
>Foo : Foo
45+
46+
foo?: number;
47+
>foo : number | undefined
48+
}
49+
50+
class FooClass<P extends Foo = Foo> {
51+
>FooClass : FooClass<P>
52+
>P : P
53+
>Foo : Foo
54+
>Foo : Foo
55+
56+
properties: Readonly<P>;
57+
>properties : Readonly<P>
58+
>Readonly : Readonly<T>
59+
>P : P
60+
61+
foo(): number {
62+
>foo : () => number
63+
64+
const { foo = 42 } = this.properties;
65+
>foo : number
66+
>42 : 42
67+
>this.properties : Readonly<P>
68+
>this : this
69+
>properties : Readonly<P>
70+
71+
return foo;
72+
>foo : number
73+
}
74+
}
75+
76+
class Test2<T extends A> {
77+
>Test2 : Test2<T>
78+
>T : T
79+
>A : A
80+
81+
attrs: Readonly<T>;
82+
>attrs : Readonly<T>
83+
>Readonly : Readonly<T>
84+
>T : T
85+
86+
m() {
87+
>m : () => T["params"]
88+
89+
return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
90+
>this.attrs.params! : T["params"]
91+
>this.attrs.params : T["params"]
92+
>this.attrs : Readonly<T>
93+
>this : this
94+
>attrs : Readonly<T>
95+
>params : T["params"]
96+
}
97+
}

0 commit comments

Comments
 (0)