GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Non-nullable types by ahejlsberg · Pull Request #7140 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Feb 18, 2016 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This PR implements support for null- and undefined-aware types and strict null checking that can be enabled with a new --strictNullChecks compiler switch. For context on this topic, see discussion in #185.

Null- and undefined-aware types

TypeScript has two special types, Null and Undefined, that have the values null and undefined respectively. Previously it was not possible to explicitly name these types, but null and undefined may now be used as type names regardless of type checking mode.

The type checker previously considered null and undefined assignable to anything. Effectively, null and undefined were valid values of every type and it wasn't possible to specifically exclude them (and therefore not possible to detect erroneous use of them). This changes in the new strict null checking mode.

In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void). So, whereas T and T | undefined are considered synonymous in regular type checking mode (because undefined is considered a subtype of any T), they are different types in strict type checking mode, and only T | undefined permits undefined values. The same is true for the relationship of T to T | null.

// Compiled with --strictNullChecks
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1;  // Ok
y = 1;  // Ok
z = 1;  // Ok
x = undefined;  // Error
y = undefined;  // Ok
z = undefined;  // Ok
x = null;  // Error
y = null;  // Error
z = null;  // Ok
x = y;  // Error
x = z;  // Error
y = x;  // Ok
y = z;  // Error
z = x;  // Ok
z = y;  // Ok

Assigned-before-use checking

In strict null checking mode the compiler requires every reference to a local variable of a type that doesn't include undefined to be preceded by an assignment to that variable in every possible preceding code path.

For example:

// Compiled with --strictNullChecks
let x: number;
let y: number | null;
let z: number | undefined;
x;  // Error, reference not preceded by assignment
y;  // Error, reference not preceded by assignment
z;  // Ok
x = 1;
y = null;
x;  // Ok
y;  // Ok

The compiler checks that variables are definitely assigned by performing control flow based type analysis. For further details on this topic, see #8010.

Optional parameters and properties

Optional parameters and properties automatically have undefined added to their types, even when their type annotations don't specifically include undefined. For example, the following two types are identical:

// Compiled with --strictNullChecks
type T1 = (x?: number) => string;              // x has type number | undefined
type T2 = (x?: number | undefined) => string;  // x has type number | undefined

Non-null and non-undefined type guards

A property access or a function call produces a compile-time error if the object or function is of a type that includes null or undefined. However, type guards are extended to support non-null and non-undefined checks. For example:

// Compiled with --strictNullChecks
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
    f(x);  // Ok, type of x is number here
}
else {
    f(x);  // Error, type of x is number? here
}
let a = x != null ? f(x) : "";  // Type of a is string
let b = x && f(x);  // Type of b is string?

Non-null and non-undefined type guards may use the ==, !=, ===, or !== operator to compare to null or undefined, as in x != null or x === undefined. The effects on subject variable types accurately reflect JavaScript semantics (e.g. double-equals operators check for both values no matter which one is specified whereas triple-equals only checks for the specified value).

Dotted names in type guards

Type guards previously only supported checking local variables and parameters. Type guards now support checking "dotted names" consisting of a variable or parameter name followed one or more property accesses. For example:

interface Options {
    location?: {
        x?: number;
        y?: number;
    };
}

function foo(options?: Options) {
    if (options && options.location && options.location.x) {
        const x = options.location.x;  // Type of x is number
    }
}

Type guards for dotted names also work with user defined type guard functions and the typeof and instanceof operators and do not depend on the --strictNullChecks compiler option.

A type guard for a dotted name has no effect following an assignment to any part of the dotted name. For example, a type guard for x.y.z will have no effect following an assignment to x, x.y, or x.y.z.

Expression operators

Expression operators permit operand types to include null and/or undefined but always produce values of non-null and non-undefined types.

// Compiled with --strictNullChecks
function sum(a: number | null, b: number | null) {
    return a + b;  // Produces value of type number
}

The && operator adds null and/or undefined to the type of the right operand depending on which are present in the type of the left operand, and the || operator removes both null and undefined from the type of the left operand in the resulting union type.

// Compiled with --strictNullChecks
interface Entity {
    name: string;
}
let x: Entity | null;
let s = x && x.name;  // s is of type string | null
let y = x || { name: "test" };  // y is of type Entity

Type widening

The null and undefined types are not widened to any in strict null checking mode.

let z = null;  // Type of z is null

In regular type checking mode the inferred type of z is any because of widening, but in strict null checking mode the inferred type of z is null (and therefore, absent a type annotation, null is the only possible value for z).

Non-null assertion operator

A new ! postfix expression operator may be used to assert that its operand is non-null and non-undefined in contexts where the type checker is unable to conclude that fact. Specifically, the operation x! produces a value of the type of x with null and undefined excluded. Similar to type assertions of the forms <T>x and x as T, the ! non-null assertion operator is simply removed in the emitted JavaScript code.

// Compiled with --strictNullChecks
function validateEntity(e: Entity?) {
    // Throw exception if e is null or invalid entity
}

function processEntity(e: Entity?) {
    validateEntity(e);
    let s = e!.name;  // Assert that e is non-null and access name
}

Compatibility

The new features are designed such that they can be used in both strict null checking mode and regular type checking mode. In particular, the null and undefined types are automatically erased from union types in regular type checking mode (because they are subtypes of all other types), and the ! non-null assertion expression operator is permitted but has no effect in regular type checking mode. Thus, declaration files that are updated to use null- and undefined-aware types can still be used in regular type checking mode for backwards compatiblity.

In practical terms, strict null checking mode requires that all files in a compilation are null- and undefined-aware. We are contemplating a pragma of some form that can be added to declaration files such that the compiler can verify that all referenced declaration files are null- and undefined-aware in a compilation that uses strict null checking.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

dallonf, weswigham, mquandalle, wwwillchen, johnfn, falsandtru, niieani, milutinovici, emilgpa, svieira, and 220 more reacted with thumbs up emoji ahmetabdi, cies, hristozov, egeozcan, RobertoUa, HerringtonDarkholme, kenhahn85, GoToLoop, keesey, lowdewijk, and 13 more reacted with laugh emoji cybrown, mquandalle, johnfn, jods4, sergey-shandar, svieira, ziahamza, supershabam, PatrickJS, fitzgen, and 94 more reacted with hooray emoji Eyas, sergey-shandar, emilgpa, ziahamza, thorn0, guncha, PatrickJS, passy, ulrichb, aymericbeaumet, and 82 more reacted with heart emoji
All reactions
  • 👍 230 reactions
  • 😄 23 reactions
  • 🎉 104 reactions
  • ❤️ 92 reactions
Anders Hejlsberg (ahejlsberg) added 18 commits February 14, 2016 18:41
@ahejlsberg
Introduce -strictNullChecks compiler option
26cc99b
@ahejlsberg
Parsing of nullable types
8e92603
@ahejlsberg
Use TypeFlags.Undefined for both undefined and null types
26e371d
@ahejlsberg
Make undefined and null assignable to each other
98b6a5a
@ahejlsberg
Accepting new baselines
e79df80
@ahejlsberg
Introduce nullable types in checker
6d6d2a1
@ahejlsberg
Display support for nullable types
f08f606
@ahejlsberg
Don't widen undefined types in unions
fa36ff8
@ahejlsberg
Support nullable types with expression operators
0d3005b
@ahejlsberg
Ensure empty array literal is assignable to array of non-null type in…
09fa3e5
… strict null mode
@ahejlsberg
Make types of optional parameters and properties nullable
41401c7
@ahejlsberg
Exclude undefined/null from flags propagation within union types
586c3ac
@ahejlsberg
Add truthy/falsey guards for nullable types
bf89530
@ahejlsberg
Add missing semicolon
bd12f1b
@ahejlsberg
Add '!' non-null assertion postfix operator
1f096bd
@ahejlsberg
Disallow line breaks between operand and '!' non-null assertion operator
46837fd
@ahejlsberg
Accepting new baselines
54ee0b1
@ahejlsberg
Correct && operator to produce nullable values
1e8a7e2
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-not-required label Feb 18, 2016
@ahejlsberg
Merge branch 'master' into strictNullChecks
44d7897
Conflicts:
	src/compiler/checker.ts
	src/compiler/types.ts
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Feb 19, 2016

Copy link
Copy Markdown
Member

This compiles without error under --strictnullchecks

let x: string;
x.substr(10);

while the equivalent

let x: string = undefined;
x.substr(10);

does not

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Feb 19, 2016

Copy link
Copy Markdown
Member Author

Ryan Cavanaugh (@RyanCavanaugh) Yup, that's the definite assignment analysis work item I've got listed in the remaining work section.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@robertknight

Robert Knight (robertknight) commented Feb 19, 2016

Copy link
Copy Markdown

Note: We use postfix notation for the ? operator to avoid ambiguities in types
like string?[] and string[]?. We may consider also supporting prefix notation similar to
the Flow type checker.

If Flow and TypeScript can agree on syntax for this and thereby enable code to be parsed with either TypeScript or Babel that would be very helpful for projects wishing to try one or the other.

mquandalle, radev, Xesued, passy, aymericbeaumet, EvansThomas, brendanzab, jin, lievenlemiengre, k0nserv, and 21 more reacted with thumbs up emoji
All reactions
  • 👍 31 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) added 2 commits February 19, 2016 09:32
@ahejlsberg
Support x == null and x != null in non-null guards. Also, allow == an…
50ea0bf
…d != in type guards.
@ahejlsberg
Accepting new baselines
d10017f
@myitcv

Paul Jolly (myitcv) commented Feb 19, 2016

Copy link
Copy Markdown

👍 to this work

type guards are extended to support non-null checks.

Can I confirm the following is also a valid type guard on x?

// Compiled with --strictNullChecks
interface Entity {
    name: string;
}
let x: Entity?;

if (x != undefined) {
    // x is of type Entity
    // guaranteed runtime safe use of x
}

Expression operators permit the operands to be both nullable and non-nullable and always produce values of non-nullable types.

Please can you explain the rationale behind this?

I would have thought the following a more intuitive interpretation:

// Compiled with --strictNullChecks
function sum(a: number?, b: number?, c: number, d: number) {
    console.log(a + b);  // error
    console.log(a + c);  // error
    console.log(c + d);  // OK
}

A new ! postfix expression operator may be used to assert that its operand is non-nullable in contexts where the type checker is unable to conclude that fact.

Given the support you describe with type guards, can you explain why this is operator is necessary?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Feb 19, 2016
consider allowing exhaustive switch statement #6155
Closed
Suggestion: Void-protected types #6981
Closed
@tejacques

Tom Jacques (tejacques) commented Feb 19, 2016

Copy link
Copy Markdown

👍 very nice to see this!

There was some discussion in other issues about the if(x) style typeguards WRT falsy values.

For example:

let x: number? = 0;
let y: string? = '';
let z: boolean? = false;

In particular, boolean? guarding with if(z) feels pretty bad, since false is one of the two legitimate values. The other are unfortunate but not terrible.

It would be nice to have a guard work on x != null to get around this issue.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Feb 19, 2016

Copy link
Copy Markdown
Member Author

Paul Jolly (@myitcv) Yes, x != undefined will work as a non-null guard (right now it only works with x != null but I will fix that).

The rationale for the expression operators is that they actually have well defined semantics for the null and undefined values. Certainly we want to support nullable values for the comparison operators, but I can see being more restrictive for the arithmetic operators. Still, not sure it merits the extra complexity.

The postfix ! assertion operator is useful in cases where the type checker can't conclude that a value is non-null but you know it to be true because of program logic. My description of the PR above gives an example of such a case.

Tom Jacques (@tejacques) An x != null type guard already works.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request May 16, 2016
Suggestion Backlog Slog, 5/16/2016 #8628
Closed
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request May 17, 2016
Support overriding type-checking compile flags on a per file basis #8405
Closed
@opensrcken opensrcken mentioned this pull request May 19, 2016
Ng2 does not compile with Typescript --strictNullChecks angular/angular#8720
Closed
@domoritz Dominik Moritz (domoritz) mentioned this pull request May 22, 2016
Make setting axis or legend = null as the default way to disable them. vega/vega-lite#1384
Merged
@jrieken Johannes Rieken (jrieken) mentioned this pull request May 26, 2016
Use non-nullable types in vscode.d.ts microsoft/vscode#6907
Closed
@jkillian Jason Killian (jkillian) mentioned this pull request May 27, 2016
Should not report an error on "null" when using in type position palantir/tslint#1265
Closed
Raj Nigam (rajington) added a commit to rajington/flow-vs-typescript that referenced this pull request Jun 4, 2016
@rajington
Add note about TypeScript 2 support for non-null
1517f63
Despite being a fan of Flow, I do think it's prudent for any recent comparison to include SOME features on the project timeline, including [non-nullable types](microsoft/TypeScript#7140) in version 2.
@rajington Raj Nigam (rajington) mentioned this pull request Jun 4, 2016
Add note about TypeScript 2 support for non-null DJCordhose/flow-vs-typescript#2
Closed
@markvandenbrink Mark van den Brink (markvandenbrink) mentioned this pull request Jun 7, 2016
Add strictNullChecks option TypeStrong/grunt-ts#360
Closed
@bradleyayers Bradley Ayers (bradleyayers) mentioned this pull request Jun 8, 2016
Add documentation for block nesting mitermayer/draft-js#9
Open
3 tasks
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jun 17, 2016
Interface Index Signatures + Strict Null Checking #9235
Closed
@yortus Troy Gerwien (yortus) mentioned this pull request Jun 18, 2016
Cannot set property 'xyz' of undefined #9247
Closed
@spalger Spencer (spalger) mentioned this pull request Jun 21, 2016
Consider static types markfinger/unfort#115
Closed
@louy Louay Akkad (louy) mentioned this pull request Jun 26, 2016
With optional it does not work with null typed-typings/npm-bluebird#20
Open
@haizz haizz mentioned this pull request Jul 11, 2016
Non-nullable types support for TypeScript 2.0 immutable-js/immutable-js#914
Closed
@gcnew Marin Marinov (gcnew) mentioned this pull request Aug 11, 2016
Should/could typeguarding work on arrays/length #10272
Closed
@yvo-niedrich Yvo Niedrich (yvo-niedrich) mentioned this pull request Aug 22, 2016
React js integration proof of concept stefanrinderle/softvis3d#30
Closed
@darcyparker Darcy Parker (darcyparker) mentioned this pull request Sep 20, 2016
null | string is not a valid disjoint type bcherny/json-schema-to-typescript#32
Closed
@spicemix spicemix mentioned this pull request Sep 23, 2016
Allow angular 2 applications to compile in strictNullChecks mode angular/angular#11844
Closed
@aluanhaddad Aluan Haddad (aluanhaddad) mentioned this pull request Sep 24, 2016
Array lookup should return T | undefined #11122
Closed
@normalser normalser mentioned this pull request Sep 27, 2016
Hashmap (unexpectedly) doesn't include undefined as a possible return value when using strictNullChecks #11186
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Sep 29, 2016
strictNullChecks still allows to assign undefined to the variable declared as number #11238
Closed
@davetemplin Dave Templin (davetemplin) mentioned this pull request Nov 13, 2016
Typescript compile errors davetemplin/web-request#10
Closed
@OliverJAsh Oliver Joseph Ash (OliverJAsh) mentioned this pull request Jan 31, 2017
Suggestion: option to include undefined in index signatures #13778
Closed
@RLovelett Ryan Lovelett (RLovelett) mentioned this pull request Feb 20, 2017
Credential Provider Chain Resolution aws/aws-sdk-js#1366
Closed
@dmitry-a-morozov Dmitry Morozov (dmitry-a-morozov) mentioned this pull request Mar 22, 2017
Flow based null check analysis for [<AllowNullLiteralAttribute>] types and alike fsharp/fslang-suggestions#552
Closed
3 of 6 tasks
@mhverbakel Martijn Verbakel (mhverbakel) mentioned this pull request Aug 29, 2017
Compile error with --strict-null-checks enabled. notenoughneon/await-semaphore#3
Closed
@orta Orta Therox (orta) mentioned this pull request Sep 26, 2017
Add typings for react-tracking DefinitelyTyped/DefinitelyTyped#20011
Merged
4 tasks done
@MartinJohns Martin Johns (MartinJohns) mentioned this pull request May 2, 2018
TypeScript shouldn't assume {[name: string]: NonUndefinedType} has no undefined properties #23817
Closed
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

TypeScript 2.0

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

20 participants

@ahejlsberg @RyanCavanaugh @robertknight @myitcv @tejacques @zpdDG4gta8XKpMCd @JsonFreeman @ivogabe @jeffreymorlan @samwgoldman @basarat @Arnavion @Igorbek @IanYates @DanielRosenwasser @danquirk @CyrusNajmabadi @tinganho @dallonf @spion
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.