Reinstate separate type kinds for 'null' and 'undefined'
This commit is contained in:
parent
04c28b09a9
commit
87ae0489eb
2 changed files with 77 additions and 42 deletions
|
@ -113,9 +113,9 @@ namespace ts {
|
|||
const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
|
||||
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
|
||||
const voidType = createIntrinsicType(TypeFlags.Void, "void");
|
||||
const undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefined, "undefined");
|
||||
const nullType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefined, "null");
|
||||
const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefined, "undefined");
|
||||
const undefinedType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
|
||||
const nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null");
|
||||
const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
|
||||
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
|
||||
|
||||
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
|
||||
|
@ -209,7 +209,7 @@ namespace ts {
|
|||
},
|
||||
"undefined": {
|
||||
type: undefinedType,
|
||||
flags: TypeFlags.ContainsUndefined
|
||||
flags: TypeFlags.ContainsUndefinedOrNull
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1883,7 +1883,7 @@ namespace ts {
|
|||
else if (type.flags & TypeFlags.Tuple) {
|
||||
writeTupleType(<TupleType>type);
|
||||
}
|
||||
else if (isNullableType(type)) {
|
||||
else if (isNullableType(type) && (<UnionType>type).types.length > 2) {
|
||||
writeType(getNonNullableType(type), TypeFormatFlags.InElementType);
|
||||
writePunctuation(writer, SyntaxKind.QuestionToken);
|
||||
}
|
||||
|
@ -4805,7 +4805,7 @@ namespace ts {
|
|||
const id = getTypeListId(typeSet);
|
||||
let type = unionTypes[id];
|
||||
if (!type) {
|
||||
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Undefined);
|
||||
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
|
||||
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | propagatedFlags);
|
||||
type.types = typeSet;
|
||||
}
|
||||
|
@ -4840,7 +4840,7 @@ namespace ts {
|
|||
const id = getTypeListId(typeSet);
|
||||
let type = intersectionTypes[id];
|
||||
if (!type) {
|
||||
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Undefined);
|
||||
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
|
||||
type = intersectionTypes[id] = <IntersectionType>createObjectType(TypeFlags.Intersection | propagatedFlags);
|
||||
type.types = typeSet;
|
||||
}
|
||||
|
@ -5502,6 +5502,9 @@ namespace ts {
|
|||
if (source.flags & TypeFlags.Undefined) {
|
||||
if (!strictNullChecks || target.flags & TypeFlags.Undefined || source === emptyArrayElementType) return Ternary.True;
|
||||
}
|
||||
if (source.flags & TypeFlags.Null) {
|
||||
if (!strictNullChecks || target.flags & TypeFlags.Null) 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)) {
|
||||
|
@ -6325,7 +6328,7 @@ namespace ts {
|
|||
// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
|
||||
// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
|
||||
return type.flags & TypeFlags.Reference && ((<TypeReference>type).target === globalArrayType || (<TypeReference>type).target === globalReadonlyArrayType) ||
|
||||
!(type.flags & TypeFlags.Undefined) && isTypeAssignableTo(type, anyReadonlyArrayType);
|
||||
!(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
|
||||
}
|
||||
|
||||
function isTupleLikeType(type: Type): boolean {
|
||||
|
@ -6344,18 +6347,18 @@ namespace ts {
|
|||
return !!(type.flags & TypeFlags.Tuple);
|
||||
}
|
||||
|
||||
function isNullableType(type: Type): boolean {
|
||||
if (type.flags & TypeFlags.Undefined) {
|
||||
return true;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
function getNullableKind(type: Type): TypeFlags {
|
||||
let flags = type.flags;
|
||||
if (flags & TypeFlags.Union) {
|
||||
for (const t of (type as UnionType).types) {
|
||||
if (t.flags & TypeFlags.Undefined) {
|
||||
return true;
|
||||
}
|
||||
flags |= t.flags;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return flags & TypeFlags.Nullable;
|
||||
}
|
||||
|
||||
function isNullableType(type: Type) {
|
||||
return getNullableKind(type) === TypeFlags.Nullable;
|
||||
}
|
||||
|
||||
function getNullableType(type: Type): Type {
|
||||
|
@ -6363,20 +6366,51 @@ namespace ts {
|
|||
return type;
|
||||
}
|
||||
if (!type.nullableType) {
|
||||
type.nullableType = isNullableType(type) ? type : getUnionType([type, undefinedType]);
|
||||
type.nullableType = isNullableType(type) ? type : getUnionType([type, undefinedType, nullType]);
|
||||
}
|
||||
return type.nullableType;
|
||||
}
|
||||
|
||||
function getNonNullableTypeFromUnionType(type: UnionType): Type {
|
||||
if (!type.nonNullableType) {
|
||||
type.nonNullableType = removeTypesFromUnionOrIntersection(type, [undefinedType, nullType]);
|
||||
function addNullableKind(type: Type, kind: TypeFlags): Type {
|
||||
if ((getNullableKind(type) & kind) !== kind) {
|
||||
const types = [type];
|
||||
if (kind & TypeFlags.Undefined) {
|
||||
types.push(undefinedType);
|
||||
}
|
||||
if (kind & TypeFlags.Null) {
|
||||
types.push(nullType);
|
||||
}
|
||||
type = getUnionType(types);
|
||||
}
|
||||
return type.nonNullableType;
|
||||
return type;
|
||||
}
|
||||
|
||||
function removeNullableKind(type: Type, kind: TypeFlags) {
|
||||
if (type.flags & TypeFlags.Union && getNullableKind(type) & kind) {
|
||||
let firstType: Type;
|
||||
let types: Type[];
|
||||
for (const t of (type as UnionType).types) {
|
||||
if (!(t.flags & kind)) {
|
||||
if (!firstType) {
|
||||
firstType = t;
|
||||
}
|
||||
else {
|
||||
if (!types) {
|
||||
types = [firstType];
|
||||
}
|
||||
types.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstType) {
|
||||
type = types ? getUnionType(types) : firstType;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function getNonNullableType(type: Type): Type {
|
||||
return strictNullChecks && type.flags & TypeFlags.Union ? getNonNullableTypeFromUnionType(type as UnionType) : type;
|
||||
return strictNullChecks ? removeNullableKind(type, TypeFlags.Nullable) : type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6433,12 +6467,12 @@ namespace ts {
|
|||
}
|
||||
|
||||
function getWidenedConstituentType(type: Type): Type {
|
||||
return type.flags & TypeFlags.Undefined ? type : getWidenedType(type);
|
||||
return type.flags & TypeFlags.Nullable ? type : getWidenedType(type);
|
||||
}
|
||||
|
||||
function getWidenedType(type: Type): Type {
|
||||
if (type.flags & TypeFlags.RequiresWidening) {
|
||||
if (type.flags & TypeFlags.Undefined) {
|
||||
if (type.flags & TypeFlags.Nullable) {
|
||||
return anyType;
|
||||
}
|
||||
if (type.flags & TypeFlags.ObjectLiteral) {
|
||||
|
@ -6490,7 +6524,7 @@ namespace ts {
|
|||
if (type.flags & TypeFlags.ObjectLiteral) {
|
||||
for (const p of getPropertiesOfObjectType(type)) {
|
||||
const t = getTypeOfSymbol(p);
|
||||
if (t.flags & TypeFlags.ContainsUndefined) {
|
||||
if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
|
||||
if (!reportWideningErrorsInType(t)) {
|
||||
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
|
||||
}
|
||||
|
@ -6534,7 +6568,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
|
||||
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefined) {
|
||||
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) {
|
||||
// Report implicit any error within type if possible, otherwise report error on declaration
|
||||
if (!reportWideningErrorsInType(type)) {
|
||||
reportImplicitAnyError(declaration, type);
|
||||
|
@ -7542,7 +7576,7 @@ namespace ts {
|
|||
checkNestedBlockScopedBinding(node, symbol);
|
||||
|
||||
const type = getTypeOfSymbol(localOrExportSymbol);
|
||||
if (strictNullChecks && !isAssignmentTarget(node) && !(type.flags & TypeFlags.Any) && !isNullableType(type)) {
|
||||
if (strictNullChecks && !isAssignmentTarget(node) && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined)) {
|
||||
checkVariableAssignedBefore(symbol, node);
|
||||
}
|
||||
return getNarrowedTypeOfReference(type, node);
|
||||
|
@ -11524,8 +11558,8 @@ namespace ts {
|
|||
// as having the primitive type Number. If one operand is the null or undefined value,
|
||||
// it is treated as having the type of the other operand.
|
||||
// The result is always of the Number primitive type.
|
||||
if (leftType.flags & TypeFlags.Undefined) leftType = rightType;
|
||||
if (rightType.flags & TypeFlags.Undefined) rightType = leftType;
|
||||
if (leftType.flags & TypeFlags.Nullable) leftType = rightType;
|
||||
if (rightType.flags & TypeFlags.Nullable) rightType = leftType;
|
||||
|
||||
leftType = getNonNullableType(leftType);
|
||||
rightType = getNonNullableType(rightType);
|
||||
|
@ -11555,8 +11589,8 @@ namespace ts {
|
|||
// or at least one of the operands to be of type Any or the String primitive type.
|
||||
|
||||
// If one operand is the null or undefined value, it is treated as having the type of the other operand.
|
||||
if (leftType.flags & TypeFlags.Undefined) leftType = rightType;
|
||||
if (rightType.flags & TypeFlags.Undefined) rightType = leftType;
|
||||
if (leftType.flags & TypeFlags.Nullable) leftType = rightType;
|
||||
if (rightType.flags & TypeFlags.Nullable) rightType = leftType;
|
||||
|
||||
leftType = getNonNullableType(leftType);
|
||||
rightType = getNonNullableType(rightType);
|
||||
|
@ -11618,7 +11652,7 @@ namespace ts {
|
|||
case SyntaxKind.InKeyword:
|
||||
return checkInExpression(left, right, leftType, rightType);
|
||||
case SyntaxKind.AmpersandAmpersandToken:
|
||||
return isNullableType(leftType) ? getNullableType(rightType) : rightType;
|
||||
return addNullableKind(rightType, getNullableKind(leftType));
|
||||
case SyntaxKind.BarBarToken:
|
||||
return getUnionType([getNonNullableType(leftType), rightType]);
|
||||
case SyntaxKind.EqualsToken:
|
||||
|
|
|
@ -2096,7 +2096,8 @@ namespace ts {
|
|||
Number = 0x00000004,
|
||||
Boolean = 0x00000008,
|
||||
Void = 0x00000010,
|
||||
Undefined = 0x00000020, // Undefined or null
|
||||
Undefined = 0x00000020,
|
||||
Null = 0x00000040,
|
||||
Enum = 0x00000080, // Enum type
|
||||
StringLiteral = 0x00000100, // String literal type
|
||||
TypeParameter = 0x00000200, // Type parameter
|
||||
|
@ -2114,7 +2115,7 @@ namespace ts {
|
|||
/* @internal */
|
||||
FreshObjectLiteral = 0x00100000, // Fresh object literal type
|
||||
/* @internal */
|
||||
ContainsUndefined = 0x00200000, // Type is or contains undefined type
|
||||
ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type
|
||||
/* @internal */
|
||||
ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type
|
||||
/* @internal */
|
||||
|
@ -2124,18 +2125,20 @@ namespace ts {
|
|||
ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties
|
||||
|
||||
/* @internal */
|
||||
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined,
|
||||
Nullable = Undefined | Null,
|
||||
/* @internal */
|
||||
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | StringLiteral | Enum,
|
||||
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null,
|
||||
/* @internal */
|
||||
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum,
|
||||
StringLike = String | StringLiteral,
|
||||
NumberLike = Number | Enum,
|
||||
ObjectType = Class | Interface | Reference | Tuple | Anonymous,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = ObjectType | Union | Intersection,
|
||||
/* @internal */
|
||||
RequiresWidening = ContainsUndefined | ContainsObjectLiteral,
|
||||
RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
|
||||
/* @internal */
|
||||
PropagatingFlags = ContainsUndefined | ContainsObjectLiteral | ContainsAnyFunctionType
|
||||
PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType
|
||||
}
|
||||
|
||||
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
|
||||
|
@ -2214,9 +2217,7 @@ namespace ts {
|
|||
resolvedProperties: SymbolTable; // Cache of resolved properties
|
||||
}
|
||||
|
||||
export interface UnionType extends UnionOrIntersectionType {
|
||||
nonNullableType?: Type; // Cached non-nullable form of type
|
||||
}
|
||||
export interface UnionType extends UnionOrIntersectionType { }
|
||||
|
||||
export interface IntersectionType extends UnionOrIntersectionType { }
|
||||
|
||||
|
|
Loading…
Reference in a new issue