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

Non-nullable types #7140

Merged
merged 65 commits into from
Mar 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
26cc99b
Introduce -strictNullChecks compiler option
ahejlsberg Feb 15, 2016
8e92603
Parsing of nullable types
ahejlsberg Feb 15, 2016
26e371d
Use TypeFlags.Undefined for both undefined and null types
ahejlsberg Feb 15, 2016
98b6a5a
Make undefined and null assignable to each other
ahejlsberg Feb 15, 2016
e79df80
Accepting new baselines
ahejlsberg Feb 15, 2016
6d6d2a1
Introduce nullable types in checker
ahejlsberg Feb 15, 2016
f08f606
Display support for nullable types
ahejlsberg Feb 15, 2016
fa36ff8
Don't widen undefined types in unions
ahejlsberg Feb 15, 2016
0d3005b
Support nullable types with expression operators
ahejlsberg Feb 15, 2016
09fa3e5
Ensure empty array literal is assignable to array of non-null type in…
ahejlsberg Feb 15, 2016
41401c7
Make types of optional parameters and properties nullable
ahejlsberg Feb 16, 2016
586c3ac
Exclude undefined/null from flags propagation within union types
ahejlsberg Feb 16, 2016
bf89530
Add truthy/falsey guards for nullable types
ahejlsberg Feb 16, 2016
bd12f1b
Add missing semicolon
ahejlsberg Feb 16, 2016
1f096bd
Add '!' non-null assertion postfix operator
ahejlsberg Feb 18, 2016
46837fd
Disallow line breaks between operand and '!' non-null assertion operator
ahejlsberg Feb 18, 2016
54ee0b1
Accepting new baselines
ahejlsberg Feb 18, 2016
1e8a7e2
Correct && operator to produce nullable values
ahejlsberg Feb 18, 2016
44d7897
Merge branch 'master' into strictNullChecks
ahejlsberg Feb 19, 2016
50ea0bf
Support x == null and x != null in non-null guards. Also, allow == an…
ahejlsberg Feb 19, 2016
d10017f
Accepting new baselines
ahejlsberg Feb 19, 2016
ed40fbf
Suport both x != null and x != undefined in non-null type guards
ahejlsberg Feb 20, 2016
74d8c40
Merge branch 'master' into strictNullChecks
ahejlsberg Feb 20, 2016
5e5381d
Merge branch 'master' into strictNullChecks
ahejlsberg Feb 21, 2016
3d7631d
Support dotted names ("x.y.z") in type guards
ahejlsberg Feb 27, 2016
82169ce
Fix getTypeOfSymbolAtLocation to handle hypothetical lookups
ahejlsberg Feb 28, 2016
7dd59ce
Accepting new baselines
ahejlsberg Feb 28, 2016
ea35932
Fix linting error
ahejlsberg Feb 28, 2016
33e3825
Assigned-before-use checking for non-nullable variables
ahejlsberg Mar 3, 2016
ea4b13b
Allow 'null' and 'undefined' as type names
ahejlsberg Mar 3, 2016
ed95811
Fix unit test
ahejlsberg Mar 3, 2016
04c28b0
Accepting new baselines
ahejlsberg Mar 3, 2016
87ae048
Reinstate separate type kinds for 'null' and 'undefined'
ahejlsberg Mar 4, 2016
c623e1f
No widening of 'null' and 'undefined' types in --strictNullChecks mode
ahejlsberg Mar 4, 2016
1302418
Accepting new baselines
ahejlsberg Mar 4, 2016
d6fcd1a
Consider for-in and for-of variables to be definitely assigned
ahejlsberg Mar 4, 2016
15b2405
Extract and lift nullability over best common supertype
ahejlsberg Mar 4, 2016
25a72d6
Removing unused functions
ahejlsberg Mar 4, 2016
64f5727
Introduce comparable (a.k.a. possibly assignable) relation
ahejlsberg Mar 5, 2016
436e70e
Accepting new baselines
ahejlsberg Mar 5, 2016
a0790fb
Add only 'undefined' to optional parameter types
ahejlsberg Mar 5, 2016
eed4093
Fix bugs in reduceLeft and reduceRight
ahejlsberg Mar 5, 2016
2762772
Include 'undefined' in return type for implicit or expressionless ret…
ahejlsberg Mar 5, 2016
097f456
Remove unused variable
ahejlsberg Mar 5, 2016
689e28d
Keep linter happy with fix in reduceLeft/reduceRight
ahejlsberg Mar 5, 2016
8db7af0
Proper handling of 'null' and 'undefined' in equals and not equals gu…
ahejlsberg Mar 5, 2016
d0e4b4a
Treat 'return' as 'return undefined' for type checking purposes
ahejlsberg Mar 5, 2016
129a4f1
Check return type includes 'undefined' in function with implicit return
ahejlsberg Mar 6, 2016
50d874e
Improve type relationship error reporting for nullable types
ahejlsberg Mar 6, 2016
0a25bb5
Make 'undefined' assignable to 'void'
ahejlsberg Mar 6, 2016
187eaae
Fix issue with narrowing exported variables
ahejlsberg Mar 7, 2016
fbda0bd
Adding another check for undefined
ahejlsberg Mar 8, 2016
868e53d
Accepting new baselines
ahejlsberg Mar 8, 2016
cf9e08b
Merge branch 'master' into strictNullChecks
ahejlsberg Mar 9, 2016
796613c
Better error message + fix assignment analysis of 'switch' statement
ahejlsberg Mar 10, 2016
dad0564
Support 'this' in type guards
ahejlsberg Mar 10, 2016
6772c34
Accepting new baselines
ahejlsberg Mar 10, 2016
9c58875
Fix linting error
ahejlsberg Mar 10, 2016
4c641c4
Add 'undefined' to type of parameter with default value in signature
ahejlsberg Mar 10, 2016
f774ecf
Remove 'undefined' from type of binding element with non-undefined de…
ahejlsberg Mar 10, 2016
b1bef15
Removing 'T?' type notation (use 'T | null | undefined' instead)
ahejlsberg Mar 14, 2016
09ad9c5
Remove 'T?' notation from type-to-string conversion
ahejlsberg Mar 14, 2016
b497243
Merge branch 'master' into strictNullChecks
ahejlsberg Mar 14, 2016
413d9a6
Merge branch 'master' into strictNullChecks
ahejlsberg Mar 21, 2016
fb6255a
Accepting new baselines
ahejlsberg Mar 21, 2016
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
967 changes: 700 additions & 267 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ namespace ts {
name: "noImplicitUseStrict",
type: "boolean",
description: Diagnostics.Do_not_emit_use_strict_directives_in_module_output
},
{
name: "strictNullChecks",
type: "boolean",
description: Diagnostics.Enable_strict_null_checks
}
];

Expand Down
20 changes: 16 additions & 4 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,14 @@ namespace ts {
const count = array.length;
if (count > 0) {
let pos = 0;
let result = arguments.length <= 2 ? array[pos] : initial;
pos++;
let result: T | U;
if (arguments.length <= 2) {
result = array[pos];
pos++;
}
else {
result = initial;
}
while (pos < count) {
result = f(<U>result, array[pos]);
pos++;
Expand All @@ -260,8 +266,14 @@ namespace ts {
if (array) {
let pos = array.length - 1;
if (pos >= 0) {
let result = arguments.length <= 2 ? array[pos] : initial;
pos--;
let result: T | U;
if (arguments.length <= 2) {
result = array[pos];
pos--;
}
else {
result = initial;
}
while (pos >= 0) {
result = f(<U>result, array[pos]);
pos--;
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ namespace ts {
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.ThisType:
case SyntaxKind.StringLiteralType:
return writeTextOfNode(currentText, type);
Expand Down
17 changes: 17 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,10 @@
"category": "Error",
"code": 2365
},
"Function lacks ending return statement and return type does not include 'undefined'.": {
"category": "Error",
"code": 2366
},
"Type parameter name cannot be '{0}'": {
"category": "Error",
"code": 2368
Expand Down Expand Up @@ -1423,6 +1427,10 @@
"category": "Error",
"code": 2453
},
"Variable '{0}' is used before being assigned.": {
"category": "Error",
"code": 2454
},
"Type argument candidate '{1}' is not a valid type argument because it is not a supertype of candidate '{0}'.": {
"category": "Error",
"code": 2455
Expand Down Expand Up @@ -1719,6 +1727,10 @@
"category": "Error",
"code": 2530
},
"Object is possibly 'null' or 'undefined'.": {
"category": "Error",
"code": 2531
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
Expand Down Expand Up @@ -2604,6 +2616,11 @@
"category": "Message",
"code": 6112
},
"Enable strict null checks.": {
"category": "Message",
"code": 6113
},

"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
30 changes: 20 additions & 10 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
case SyntaxKind.JsxSpreadAttribute:
case SyntaxKind.JsxExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.NonNullExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
Expand Down Expand Up @@ -2077,8 +2078,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
function parenthesizeForAccess(expr: Expression): LeftHandSideExpression {
// When diagnosing whether the expression needs parentheses, the decision should be based
// on the innermost expression in a chain of nested type assertions.
while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) {
expr = (<AssertionExpression>expr).expression;
while (expr.kind === SyntaxKind.TypeAssertionExpression ||
expr.kind === SyntaxKind.AsExpression ||
expr.kind === SyntaxKind.NonNullExpression) {
expr = (<AssertionExpression | NonNullExpression>expr).expression;
}

// isLeftHandSideExpression is almost the correct criterion for when it is not necessary
Expand Down Expand Up @@ -2343,8 +2346,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}

function skipParentheses(node: Expression): Expression {
while (node.kind === SyntaxKind.ParenthesizedExpression || node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) {
node = (<ParenthesizedExpression | AssertionExpression>node).expression;
while (node.kind === SyntaxKind.ParenthesizedExpression ||
node.kind === SyntaxKind.TypeAssertionExpression ||
node.kind === SyntaxKind.AsExpression ||
node.kind === SyntaxKind.NonNullExpression) {
node = (<ParenthesizedExpression | AssertionExpression | NonNullExpression>node).expression;
}
return node;
}
Expand Down Expand Up @@ -2518,13 +2524,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
// not the user. If we didn't want them, the emitter would not have put them
// there.
if (!nodeIsSynthesized(node) && node.parent.kind !== SyntaxKind.ArrowFunction) {
if (node.expression.kind === SyntaxKind.TypeAssertionExpression || node.expression.kind === SyntaxKind.AsExpression) {
let operand = (<TypeAssertion>node.expression).expression;
if (node.expression.kind === SyntaxKind.TypeAssertionExpression ||
node.expression.kind === SyntaxKind.AsExpression ||
node.expression.kind === SyntaxKind.NonNullExpression) {
let operand = (<TypeAssertion | NonNullExpression>node.expression).expression;

// Make sure we consider all nested cast expressions, e.g.:
// (<any><number><any>-A).x;
while (operand.kind === SyntaxKind.TypeAssertionExpression || operand.kind === SyntaxKind.AsExpression) {
operand = (<TypeAssertion>operand).expression;
while (operand.kind === SyntaxKind.TypeAssertionExpression ||
operand.kind === SyntaxKind.AsExpression ||
operand.kind === SyntaxKind.NonNullExpression) {
operand = (<TypeAssertion | NonNullExpression>operand).expression;
}

// We have an expression of the form: (<Type>SubExpr)
Expand Down Expand Up @@ -7928,9 +7938,9 @@ const _super = (function (geti, seti) {
case SyntaxKind.TaggedTemplateExpression:
return emitTaggedTemplateExpression(<TaggedTemplateExpression>node);
case SyntaxKind.TypeAssertionExpression:
return emit((<TypeAssertion>node).expression);
case SyntaxKind.AsExpression:
return emit((<AsExpression>node).expression);
case SyntaxKind.NonNullExpression:
return emit((<AssertionExpression | NonNullExpression>node).expression);
case SyntaxKind.ParenthesizedExpression:
return emitParenExpression(<ParenthesizedExpression>node);
case SyntaxKind.FunctionDeclaration:
Expand Down
14 changes: 14 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ namespace ts {
case SyntaxKind.AsExpression:
return visitNode(cbNode, (<AsExpression>node).expression) ||
visitNode(cbNode, (<AsExpression>node).type);
case SyntaxKind.NonNullExpression:
return visitNode(cbNode, (<NonNullExpression>node).expression);
case SyntaxKind.ConditionalExpression:
return visitNode(cbNode, (<ConditionalExpression>node).condition) ||
visitNode(cbNode, (<ConditionalExpression>node).questionToken) ||
Expand Down Expand Up @@ -2361,12 +2363,14 @@ namespace ts {
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.UndefinedKeyword:
// If these are followed by a dot, then parse these out as a dotted type reference instead.
const node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReference();
case SyntaxKind.StringLiteral:
return parseStringLiteralTypeNode();
case SyntaxKind.VoidKeyword:
case SyntaxKind.NullKeyword:
return parseTokenNode<TypeNode>();
case SyntaxKind.ThisKeyword: {
const thisKeyword = parseThisTypeNode();
Expand Down Expand Up @@ -2398,6 +2402,8 @@ namespace ts {
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.OpenBraceToken:
Expand Down Expand Up @@ -3724,6 +3730,14 @@ namespace ts {
continue;
}

if (token === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
nextToken();
const nonNullExpression = <NonNullExpression>createNode(SyntaxKind.NonNullExpression, expression.pos);
nonNullExpression.expression = expression;
expression = finishNode(nonNullExpression);
continue;
}

// when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName
if (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken)) {
const indexedAccess = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression, expression.pos);
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ namespace ts {
"try": SyntaxKind.TryKeyword,
"type": SyntaxKind.TypeKeyword,
"typeof": SyntaxKind.TypeOfKeyword,
"undefined": SyntaxKind.UndefinedKeyword,
"var": SyntaxKind.VarKeyword,
"void": SyntaxKind.VoidKeyword,
"while": SyntaxKind.WhileKeyword,
Expand Down
25 changes: 22 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ namespace ts {
StringKeyword,
SymbolKeyword,
TypeKeyword,
UndefinedKeyword,
FromKeyword,
GlobalKeyword,
OfKeyword, // LastKeyword and LastToken
Expand Down Expand Up @@ -240,6 +241,7 @@ namespace ts {
OmittedExpression,
ExpressionWithTypeArguments,
AsExpression,
NonNullExpression,

// Misc
TemplateSpan,
Expand Down Expand Up @@ -475,6 +477,11 @@ namespace ts {
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
}

// Transient identifier node (marked by id === -1)
export interface TransientIdentifier extends Identifier {
resolvedSymbol: Symbol;
}

// @kind(SyntaxKind.QualifiedName)
export interface QualifiedName extends Node {
// Must have same layout as PropertyAccess
Expand Down Expand Up @@ -968,6 +975,8 @@ namespace ts {
name: Identifier;
}

export type IdentifierOrPropertyAccess = Identifier | PropertyAccessExpression;

// @kind(SyntaxKind.ElementAccessExpression)
export interface ElementAccessExpression extends MemberExpression {
expression: LeftHandSideExpression;
Expand Down Expand Up @@ -1012,6 +1021,11 @@ namespace ts {

export type AssertionExpression = TypeAssertion | AsExpression;

// @kind(SyntaxKind.NonNullExpression)
export interface NonNullExpression extends LeftHandSideExpression {
expression: Expression;
}

/// A JSX expression of the form <TagName attrs>...</TagName>
// @kind(SyntaxKind.JsxElement)
export interface JsxElement extends PrimaryExpression {
Expand Down Expand Up @@ -2029,7 +2043,9 @@ namespace ts {
exportsChecked?: boolean; // True if exports of external module have been checked
isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration
bindingElement?: BindingElement; // Binding element associated with property symbol
exportsSomeValue?: boolean; // true if module exports some value (not just types)
exportsSomeValue?: boolean; // True if module exports some value (not just types)
firstAssignmentChecked?: boolean; // True if first assignment node has been computed
firstAssignment?: Node; // First assignment node (undefined if no assignments)
}

/* @internal */
Expand Down Expand Up @@ -2072,7 +2088,7 @@ namespace ts {
isVisible?: boolean; // Is this node visible
generatedName?: string; // Generated name for module, enum, or import declaration
generatedNames?: Map<string>; // Generated names table for source file
assignmentChecks?: Map<boolean>; // Cache of assignment checks
assignmentMap?: Map<boolean>; // Cached map of references assigned within this node
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
importOnRightSide?: Symbol; // for import declarations - import that appear on the right side
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
Expand Down Expand Up @@ -2106,7 +2122,7 @@ namespace ts {
/* @internal */
FreshObjectLiteral = 0x00100000, // Fresh object literal type
/* @internal */
ContainsUndefinedOrNull = 0x00200000, // Type is or contains Undefined or Null type
ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type
/* @internal */
ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type
/* @internal */
Expand All @@ -2115,6 +2131,8 @@ namespace ts {
ThisType = 0x02000000, // This type
ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties

/* @internal */
Nullable = Undefined | Null,
/* @internal */
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
/* @internal */
Expand Down Expand Up @@ -2439,6 +2457,7 @@ namespace ts {
allowSyntheticDefaultImports?: boolean;
allowJs?: boolean;
noImplicitUseStrict?: boolean;
strictNullChecks?: boolean;
lib?: string[];
/* @internal */ stripInternal?: boolean;

Expand Down
3 changes: 3 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ namespace ts {
case SyntaxKind.StringKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.UndefinedKeyword:
return true;
case SyntaxKind.VoidKeyword:
return node.parent.kind !== SyntaxKind.VoidExpression;
Expand Down Expand Up @@ -968,6 +969,7 @@ namespace ts {
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.NonNullExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassExpression:
Expand Down Expand Up @@ -2406,6 +2408,7 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.NonNullExpression:
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.TaggedTemplateExpression:
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/arrayLiteralWidened.types
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var a = [undefined, undefined];

var b = [[], [null, null]]; // any[][]
>b : any[][]
>[[], [null, null]] : null[][]
>[[], [null, null]] : undefined[][]
>[] : undefined[]
>[null, null] : null[]
>null : null
Expand Down
Loading