{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/7140","repository_url":"https://api.github.com/repos/Microsoft/TypeScript","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/7140/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/7140/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/7140/events","html_url":"https://github.com/Microsoft/TypeScript/pull/7140","id":134699594,"number":7140,"title":"Non-nullable types","user":{"login":"ahejlsberg","id":4226954,"avatar_url":"https://avatars.githubusercontent.com/u/4226954?v=3","gravatar_id":"","url":"https://api.github.com/users/ahejlsberg","html_url":"https://github.com/ahejlsberg","followers_url":"https://api.github.com/users/ahejlsberg/followers","following_url":"https://api.github.com/users/ahejlsberg/following{/other_user}","gists_url":"https://api.github.com/users/ahejlsberg/gists{/gist_id}","starred_url":"https://api.github.com/users/ahejlsberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ahejlsberg/subscriptions","organizations_url":"https://api.github.com/users/ahejlsberg/orgs","repos_url":"https://api.github.com/users/ahejlsberg/repos","events_url":"https://api.github.com/users/ahejlsberg/events{/privacy}","received_events_url":"https://api.github.com/users/ahejlsberg/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/cla-not-required","name":"cla-not-required","color":"009800"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":67,"created_at":"2016-02-18T21:18:33Z","updated_at":"2016-03-17T15:55:12Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/Microsoft/TypeScript/pulls/7140","html_url":"https://github.com/Microsoft/TypeScript/pull/7140","diff_url":"https://github.com/Microsoft/TypeScript/pull/7140.diff","patch_url":"https://github.com/Microsoft/TypeScript/pull/7140.patch"},"body":"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.\r\n\r\n### Null- and undefined-aware types\r\n\r\nTypeScript 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.\r\n\r\nThe 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.\r\n\r\nIn 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`.\r\n\r\n```Typescript\r\n// Compiled with --strictNullChecks\r\nlet x: number;\r\nlet y: number | undefined;\r\nlet z: number | null | undefined;\r\nx = 1;  // Ok\r\ny = 1;  // Ok\r\nz = 1;  // Ok\r\nx = undefined;  // Error\r\ny = undefined;  // Ok\r\nz = undefined;  // Ok\r\nx = null;  // Error\r\ny = null;  // Error\r\nz = null;  // Ok\r\nx = y;  // Error\r\nx = z;  // Error\r\ny = x;  // Ok\r\ny = z;  // Error\r\nz = x;  // Ok\r\nz = y;  // Ok\r\n```\r\n\r\n### Assigned-before-use checking\r\n\r\nIn strict null checking mode the compiler requires every reference to a local variable of a type that doesn't include `undefined` to be lexically preceded by at least one assignment to that variable. For example:\r\n\r\n```Typescript\r\n// Compiled with --strictNullChecks\r\nlet x: number;\r\nlet y: number | null;\r\nlet z: number | undefined;\r\nx;  // Error, reference not preceded by assignment\r\ny;  // Error, reference not preceded by assignment\r\nz;  // Ok\r\nx = 1;\r\ny = null;\r\nx;  // Ok\r\ny;  // Ok\r\n```\r\n\r\nThe analysis simply checks that an initializer or an assignment exists *somewhere* before each reference. It doesn't check that *all* possible code paths assign a value. For example, a missing assignment in one branch of an `if` statement is currently not flagged. We may implement more complete definite assignment analysis at some point, but it is beyond the scope of this PR. (Note that any solution we implement is unlikely to ever find *all* issues because it is in practice impossible to exhaustively reason about the effects of references to local variables from nested functions.)\r\n\r\n### Optional parameters and properties\r\n\r\nOptional 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:\r\n\r\n```Typescript\r\n// Compiled with --strictNullChecks\r\ntype T1 = (x?: number) => string;              // x has type number | undefined\r\ntype T2 = (x?: number | undefined) => string;  // x has type number | undefined\r\n```\r\n\r\n### Non-null and non-undefined type guards\r\n\r\nA 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:\r\n\r\n```Typescript\r\n// Compiled with --strictNullChecks\r\ndeclare function f(x: number): string;\r\nlet x: number | null | undefined;\r\nif (x) {\r\n    f(x);  // Ok, type of x is number here\r\n}\r\nelse {\r\n    f(x);  // Error, type of x is number? here\r\n}\r\nlet a = x != null ? f(x) : \"\";  // Type of a is string\r\nlet b = x && f(x);  // Type of b is string?\r\n```\r\n\r\nNon-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).\r\n\r\n### Dotted names in type guards\r\n\r\nType 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:\r\n\r\n```Typescript\r\ninterface Options {\r\n    location?: {\r\n        x?: number;\r\n        y?: number;\r\n    };\r\n}\r\n\r\nfunction foo(options?: Options) {\r\n    if (options && options.location && options.location.x) {\r\n        const x = options.location.x;  // Type of x is number\r\n    }\r\n}\r\n```\r\n\r\nType 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.\r\n\r\nA type guard for a dotted name has no effect if any part of the dotted name is the target of an assignment within the guarded code. For example, a type guard for `x.y.z` will have no effect if the guarded code contains any assignments to `x`, `x.y`, or `x.y.z`.\r\n\r\n### Expression operators\r\n\r\nExpression operators permit operand types to include `null` and/or `undefined` but always produce values of non-null and non-undefined types.\r\n\r\n```Typescript\r\n// Compiled with --strictNullChecks\r\nfunction sum(a: number | null, b: number | null) {\r\n    return a + b;  // Produces value of type number\r\n}\r\n```\r\n\r\nThe `&&` 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.\r\n\r\n```Typescript\r\n// Compiled with --strictNullChecks\r\ninterface Entity {\r\n    name: string;\r\n}\r\nlet x: Entity | null;\r\nlet s = x && x.name;  // s is of type string | null\r\nlet y = x || { name: \"test\" };  // y is of type Entity\r\n```\r\n\r\n### Type widening\r\n\r\nThe `null` and `undefined` types are *not* widened to `any` in strict null checking mode.\r\n\r\n```TypeScript\r\nlet z = null;  // Type of z is null\r\n```\r\n\r\nIn 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`).\r\n\r\n### Non-null assertion operator\r\n\r\nA 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.\r\n\r\n```TypeScript\r\n// Compiled with --strictNullChecks\r\nfunction validateEntity(e: Entity?) {\r\n    // Throw exception if e is null or invalid entity\r\n}\r\n\r\nfunction processEntity(e: Entity?) {\r\n    validateEntity(e);\r\n    let s = e!.name;  // Assert that e is non-null and access name\r\n}\r\n```\r\n\r\n### Compatibility\r\n\r\nThe 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.\r\n\r\nIn 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.\r\n\r\n### Remaining work\r\n\r\n* Tests.\r\n* Update lib.d.ts to use null- and undefined-aware types.\r\n* Pragma to indicate declaration file has been updated to use null- and undefined-aware types.\r\n"},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/197945151","html_url":"https://github.com/Microsoft/TypeScript/pull/7140#issuecomment-197945151","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/7140","id":197945151,"user":{"login":"malibuzios","id":17719551,"avatar_url":"https://avatars.githubusercontent.com/u/17719551?v=3","gravatar_id":"","url":"https://api.github.com/users/malibuzios","html_url":"https://github.com/malibuzios","followers_url":"https://api.github.com/users/malibuzios/followers","following_url":"https://api.github.com/users/malibuzios/following{/other_user}","gists_url":"https://api.github.com/users/malibuzios/gists{/gist_id}","starred_url":"https://api.github.com/users/malibuzios/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/malibuzios/subscriptions","organizations_url":"https://api.github.com/users/malibuzios/orgs","repos_url":"https://api.github.com/users/malibuzios/repos","events_url":"https://api.github.com/users/malibuzios/events{/privacy}","received_events_url":"https://api.github.com/users/malibuzios/received_events","type":"User","site_admin":false},"created_at":"2016-03-17T15:55:11Z","updated_at":"2016-03-17T15:55:11Z","body":"@jods4 \r\nI can understand different people may different views and if there is no consensus, then it is usually wiser to wait until a later stage, where perhaps more insight would be accumulated over real-world cases and usage.\r\n\r\nI actually made a special effort to express my my opinion that `T?` would become less intuitive if the question mark is used to denote something else than optionality (i.e. denoting  `T | null` or `T | null | undefined`), which I felt may become a bit confusing for beginners, and would feel redundant if it was set to `T | undefined` - that's why I said I preferred nothing at all. \r\n\r\nThe `let x?: T` case was actually one of those places where I did not predict would become very controversial. It turns out it is, to some degree, and even some Microsoft members have expressed some doubts about it (see [these design meeting notes](https://github.com/Microsoft/TypeScript/issues/7488#issue-140333724) under \"optional members on new members\"):\r\n\r\n>Variables are more of a questionable (pun intended) case - not entirely certain.\r\n> * It seems like this would actually give you a case to say that your variable can be undefined and reflects that in the type.\r\n\r\nMaybe I'm not seeing it broadly enough, but it seems there are many places in my code where when passing a value to a function which has an optional parameter, it would be natural to write something like:\r\n```ts\r\nfunction readSomeData(offset?: number)\r\n  ..\r\n}\r\n\r\nfunction readMore() {\r\n  let myOffset?: number;\r\n\r\n  if (someConditionIsTrue())\r\n    myOffset = getOffset(...);\r\n  \r\n  return readSomeData(myOffset)\r\n}\r\n```\r\nI view the question mark in `let myOffset?: number;` as expressing the fact that `myOffset` may not be **assigned** at all (unlike the same notation in a class member, it is guranteed to exist - meaning in practice it is 'explicitly' set to `undefined`, despite the fact it may be never assigned). Think about it, even if it was, say, nulllable, it would still need to be assigned in order to have a value `null`. That's why this is mostly an analogy, not an exact parallel to the same notation in function parameters and interfaces/classes.\r\n\r\nA roughly similar sort of symmetry is apparent in interfaces and implementing classes:\r\n```ts\r\ninterface MyInterface {\r\n  x?: number;\r\n}\r\n\r\nclass MyClass implements MyInterface {\r\n  x?: number;\r\n}\r\n```"}}