Skip to content
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

Use global NonNullable type for nonnull types #22096

Merged
Merged
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
15 changes: 13 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ namespace ts {
let anyArrayType: Type;
let autoArrayType: Type;
let anyReadonlyArrayType: Type;
let deferredGlobalNonNullableTypeAlias: Symbol;

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

function getGlobalNonNullableTypeInstantiation(type: Type) {
if (!deferredGlobalNonNullableTypeAlias) {
deferredGlobalNonNullableTypeAlias = getGlobalSymbol("NonNullable" as __String, SymbolFlags.TypeAlias, /*diagnostic*/ undefined) || unknownSymbol;
}
// Use NonNullable global type alias if available to improve quick info/declaration emit
if (deferredGlobalNonNullableTypeAlias !== unknownSymbol) {
return getTypeAliasInstantiation(deferredGlobalNonNullableTypeAlias, [type]);
}
return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); // Type alias unavailable, fall back to non-higherorder behavior
}

function getNonNullableType(type: Type): Type {
return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type;
}

/**
Expand Down Expand Up @@ -13314,7 +13326,6 @@ namespace ts {
return parent.kind === SyntaxKind.PropertyAccessExpression ||
parent.kind === SyntaxKind.CallExpression && (<CallExpression>parent).expression === node ||
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node ||
parent.kind === SyntaxKind.NonNullExpression ||
parent.kind === SyntaxKind.BindingElement && (<BindingElement>parent).name === node && !!(<BindingElement>parent).initializer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ function fn<T extends string | undefined, U extends string>(one: T, two: U) {
foo(one!);
>foo(one!) : void
>foo : (p: string) => void
>one! : string
>one : string | undefined
>one! : NonNullable<T>
>one : T

foo(two!);
>foo(two!) : void
>foo : (p: string) => void
>two! : U
>two! : NonNullable<U>
>two : U

foo(three!); // this line is the important one
>foo(three!) : void
>foo : (p: string) => void
>three! : string
>three : string
>three! : NonNullable<T> | NonNullable<U>
>three : T | U
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
error TS2318: Cannot find global type 'Array'.
error TS2318: Cannot find global type 'Boolean'.
error TS2318: Cannot find global type 'Function'.
error TS2318: Cannot find global type 'IArguments'.
error TS2318: Cannot find global type 'Number'.
error TS2318: Cannot find global type 'Object'.
error TS2318: Cannot find global type 'RegExp'.
error TS2318: Cannot find global type 'String'.
tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts(10,28): error TS2339: Property 'name' does not exist on type 'T["params"]'.


!!! error TS2318: Cannot find global type 'Array'.
!!! error TS2318: Cannot find global type 'Boolean'.
!!! error TS2318: Cannot find global type 'Function'.
!!! error TS2318: Cannot find global type 'IArguments'.
!!! error TS2318: Cannot find global type 'Number'.
!!! error TS2318: Cannot find global type 'Object'.
!!! error TS2318: Cannot find global type 'RegExp'.
!!! error TS2318: Cannot find global type 'String'.
==== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts (1 errors) ====
type Readonly<T> = {readonly [K in keyof T]: T[K]}
interface A {
params?: { name: string; };
}

class Test<T extends A> {
attrs: Readonly<T>;

m() {
this.attrs.params!.name;
~~~~
!!! error TS2339: Property 'name' does not exist on type 'T["params"]'.
}
}

interface Foo {
foo?: number;
}

class FooClass<P extends Foo = Foo> {
properties: Readonly<P>;

foo(): number {
const { foo = 42 } = this.properties;
return foo;
}
}

class Test2<T extends A> {
attrs: Readonly<T>;

m() {
return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
}
}
61 changes: 61 additions & 0 deletions tests/baselines/reference/strictNullNotNullIndexTypeNoLib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//// [strictNullNotNullIndexTypeNoLib.ts]
type Readonly<T> = {readonly [K in keyof T]: T[K]}
interface A {
params?: { name: string; };
}

class Test<T extends A> {
attrs: Readonly<T>;

m() {
this.attrs.params!.name;
}
}

interface Foo {
foo?: number;
}

class FooClass<P extends Foo = Foo> {
properties: Readonly<P>;

foo(): number {
const { foo = 42 } = this.properties;
return foo;
}
}

class Test2<T extends A> {
attrs: Readonly<T>;

m() {
return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
}
}

//// [strictNullNotNullIndexTypeNoLib.js]
var Test = /** @class */ (function () {
function Test() {
}
Test.prototype.m = function () {
this.attrs.params.name;
};
return Test;
}());
var FooClass = /** @class */ (function () {
function FooClass() {
}
FooClass.prototype.foo = function () {
var _a = this.properties.foo, foo = _a === void 0 ? 42 : _a;
return foo;
};
return FooClass;
}());
var Test2 = /** @class */ (function () {
function Test2() {
}
Test2.prototype.m = function () {
return this.attrs.params; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
};
return Test2;
}());
92 changes: 92 additions & 0 deletions tests/baselines/reference/strictNullNotNullIndexTypeNoLib.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
=== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts ===
type Readonly<T> = {readonly [K in keyof T]: T[K]}
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 14))
>K : Symbol(K, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 30))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 14))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 14))
>K : Symbol(K, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 30))

interface A {
>A : Symbol(A, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 50))

params?: { name: string; };
>params : Symbol(A.params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
>name : Symbol(name, Decl(strictNullNotNullIndexTypeNoLib.ts, 2, 14))
}

class Test<T extends A> {
>Test : Symbol(Test, Decl(strictNullNotNullIndexTypeNoLib.ts, 3, 1))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 11))
>A : Symbol(A, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 50))

attrs: Readonly<T>;
>attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25))
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 11))

m() {
>m : Symbol(Test.m, Decl(strictNullNotNullIndexTypeNoLib.ts, 6, 23))

this.attrs.params!.name;
>this.attrs.params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
>this.attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25))
>this : Symbol(Test, Decl(strictNullNotNullIndexTypeNoLib.ts, 3, 1))
>attrs : Symbol(Test.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 5, 25))
>params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
}
}

interface Foo {
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 11, 1))

foo?: number;
>foo : Symbol(Foo.foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 13, 15))
}

class FooClass<P extends Foo = Foo> {
>FooClass : Symbol(FooClass, Decl(strictNullNotNullIndexTypeNoLib.ts, 15, 1))
>P : Symbol(P, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 15))
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 11, 1))
>Foo : Symbol(Foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 11, 1))

properties: Readonly<P>;
>properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 37))
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
>P : Symbol(P, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 15))

foo(): number {
>foo : Symbol(FooClass.foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 18, 28))

const { foo = 42 } = this.properties;
>foo : Symbol(foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 21, 15))
>this.properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 37))
>this : Symbol(FooClass, Decl(strictNullNotNullIndexTypeNoLib.ts, 15, 1))
>properties : Symbol(FooClass.properties, Decl(strictNullNotNullIndexTypeNoLib.ts, 17, 37))

return foo;
>foo : Symbol(foo, Decl(strictNullNotNullIndexTypeNoLib.ts, 21, 15))
}
}

class Test2<T extends A> {
>Test2 : Symbol(Test2, Decl(strictNullNotNullIndexTypeNoLib.ts, 24, 1))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 12))
>A : Symbol(A, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 50))

attrs: Readonly<T>;
>attrs : Symbol(Test2.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 26))
>Readonly : Symbol(Readonly, Decl(strictNullNotNullIndexTypeNoLib.ts, 0, 0))
>T : Symbol(T, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 12))

m() {
>m : Symbol(Test2.m, Decl(strictNullNotNullIndexTypeNoLib.ts, 27, 23))

return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
>this.attrs.params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
>this.attrs : Symbol(Test2.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 26))
>this : Symbol(Test2, Decl(strictNullNotNullIndexTypeNoLib.ts, 24, 1))
>attrs : Symbol(Test2.attrs, Decl(strictNullNotNullIndexTypeNoLib.ts, 26, 26))
>params : Symbol(params, Decl(strictNullNotNullIndexTypeNoLib.ts, 1, 13))
}
}
97 changes: 97 additions & 0 deletions tests/baselines/reference/strictNullNotNullIndexTypeNoLib.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
=== tests/cases/compiler/strictNullNotNullIndexTypeNoLib.ts ===
type Readonly<T> = {readonly [K in keyof T]: T[K]}
>Readonly : Readonly<T>
>T : T
>K : K
>T : T
>T : T
>K : K

interface A {
>A : A

params?: { name: string; };
>params : { name: string; } | undefined
>name : string
}

class Test<T extends A> {
>Test : Test<T>
>T : T
>A : A

attrs: Readonly<T>;
>attrs : Readonly<T>
>Readonly : Readonly<T>
>T : T

m() {
>m : () => void

this.attrs.params!.name;
>this.attrs.params!.name : any
>this.attrs.params! : T["params"]
>this.attrs.params : T["params"]
>this.attrs : Readonly<T>
>this : this
>attrs : Readonly<T>
>params : T["params"]
>name : any
}
}

interface Foo {
>Foo : Foo

foo?: number;
>foo : number | undefined
}

class FooClass<P extends Foo = Foo> {
>FooClass : FooClass<P>
>P : P
>Foo : Foo
>Foo : Foo

properties: Readonly<P>;
>properties : Readonly<P>
>Readonly : Readonly<T>
>P : P

foo(): number {
>foo : () => number

const { foo = 42 } = this.properties;
>foo : number
>42 : 42
>this.properties : Readonly<P>
>this : this
>properties : Readonly<P>

return foo;
>foo : number
}
}

class Test2<T extends A> {
>Test2 : Test2<T>
>T : T
>A : A

attrs: Readonly<T>;
>attrs : Readonly<T>
>Readonly : Readonly<T>
>T : T

m() {
>m : () => T["params"]

return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally
>this.attrs.params! : T["params"]
>this.attrs.params : T["params"]
>this.attrs : Readonly<T>
>this : this
>attrs : Readonly<T>
>params : T["params"]
}
}
Loading