From 6d6d2a11bc07c5ab15462362e27164446a3d05bd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 15 Feb 2016 09:34:14 -0800 Subject: [PATCH] Introduce nullable types in checker --- src/compiler/checker.ts | 61 ++++++++++++++++++++++++++++++++++++++--- src/compiler/types.ts | 5 +++- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2de8c86e5fd..4d930b35667 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -51,6 +51,8 @@ namespace ts { const languageVersion = compilerOptions.target || ScriptTarget.ES3; const modulekind = getEmitModuleKind(compilerOptions); const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System; + const strictNullChecks = compilerOptions.strictNullChecks; + const emitResolver = createResolver(); @@ -2340,6 +2342,7 @@ namespace ts { case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: case SyntaxKind.ParenthesizedType: + case SyntaxKind.NullableType: return isDeclarationVisible(node.parent); // Default binding, import specifier and namespace import is visible @@ -4664,6 +4667,14 @@ namespace ts { return links.resolvedType; } + function getTypeFromNullableTypeNode(node: NullableTypeNode): Type { + const links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = getNullableType(getTypeFromTypeNode(node.type)); + } + return links.resolvedType; + } + function addTypeToSet(typeSet: Type[], type: Type, typeSetKind: TypeFlags) { if (type.flags & typeSetKind) { addTypesToSet(typeSet, (type).types, typeSetKind); @@ -4736,8 +4747,10 @@ namespace ts { return anyType; } if (noSubtypeReduction) { - removeAllButLast(typeSet, undefinedType); - removeAllButLast(typeSet, nullType); + if (!strictNullChecks) { + removeAllButLast(typeSet, undefinedType); + removeAllButLast(typeSet, nullType); + } } else { removeSubtypes(typeSet); @@ -4920,6 +4933,8 @@ namespace ts { return getTypeFromUnionTypeNode(node); case SyntaxKind.IntersectionType: return getTypeFromIntersectionTypeNode(node); + case SyntaxKind.NullableType: + return getTypeFromNullableTypeNode(node); case SyntaxKind.ParenthesizedType: case SyntaxKind.JSDocNullableType: case SyntaxKind.JSDocNonNullableType: @@ -5415,7 +5430,9 @@ namespace ts { } if (isTypeAny(target)) return Ternary.True; - if (source.flags & TypeFlags.Undefined) return Ternary.True; + if (source.flags & TypeFlags.Undefined) { + if (!strictNullChecks || target.flags & TypeFlags.Undefined) return Ternary.True; + } if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True; if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) { if (result = enumRelatedTo(source, target)) { @@ -6262,6 +6279,41 @@ namespace ts { return !!(type.flags & TypeFlags.Tuple); } + function isNullableType(type: Type): boolean { + if (type.flags & TypeFlags.Undefined) { + return true; + } + if (type.flags & TypeFlags.Union) { + for (const t of (type as UnionType).types) { + if (t.flags & TypeFlags.Undefined) { + return true; + } + } + } + return false; + } + + function getNullableType(type: Type): Type { + if (!strictNullChecks) { + return type; + } + if (!type.nullableType) { + type.nullableType = isNullableType(type) ? type : getUnionType([type, undefinedType]); + } + return type.nullableType; + } + + function getNonNullableTypeFromUnionType(type: UnionType): Type { + if (!type.nonNullableType) { + type.nonNullableType = removeTypesFromUnionOrIntersection(type, [undefinedType, nullType]); + } + return type.nonNullableType; + } + + function getNonNullableType(type: Type): Type { + return strictNullChecks && type.flags & TypeFlags.Union ? getNonNullableTypeFromUnionType(type as UnionType) : type; + } + function getRegularTypeOfObjectLiteral(type: Type): Type { if (type.flags & TypeFlags.FreshObjectLiteral) { let regularType = (type).regularType; @@ -14988,7 +15040,8 @@ namespace ts { case SyntaxKind.IntersectionType: return checkUnionOrIntersectionType(node); case SyntaxKind.ParenthesizedType: - return checkSourceElement((node).type); + case SyntaxKind.NullableType: + return checkSourceElement((node).type); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.Block: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 17a50a1c571..d7abcc02963 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2125,6 +2125,7 @@ namespace ts { /* @internal */ id: number; // Unique ID symbol?: Symbol; // Symbol associated with type (if any) pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any) + nullableType?: Type; // Cached nullable form of this type } /* @internal */ @@ -2197,7 +2198,9 @@ namespace ts { resolvedProperties: SymbolTable; // Cache of resolved properties } - export interface UnionType extends UnionOrIntersectionType { } + export interface UnionType extends UnionOrIntersectionType { + nonNullableType?: Type; // Cached non-nullable form of type + } export interface IntersectionType extends UnionOrIntersectionType { }