diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a3e65b4eb65..be72d9d657a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -309,8 +309,8 @@ namespace ts { let flowLoopCount = 0; let visitedFlowCount = 0; - const emptyStringType = getLiteralTypeForText(TypeFlags.StringLiteral, ""); - const zeroType = getLiteralTypeForText(TypeFlags.NumberLiteral, "0"); + const emptyStringType = getLiteralType(""); + const zeroType = getLiteralType(0); const resolutionTargets: TypeSystemEntity[] = []; const resolutionResults: boolean[] = []; @@ -1869,7 +1869,7 @@ namespace ts { } function createTypeofType() { - return getUnionType(convertToArray(typeofEQFacts.keys(), s => getLiteralTypeForText(TypeFlags.StringLiteral, s))); + return getUnionType(convertToArray(typeofEQFacts.keys(), getLiteralType)); } // A reserved member name starts with two underscores, but the third character cannot be an underscore @@ -2320,11 +2320,8 @@ namespace ts { const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } - if (type.flags & (TypeFlags.StringLiteral)) { - return createLiteralTypeNode((createLiteral((type).text))); - } - if (type.flags & (TypeFlags.NumberLiteral)) { - return createLiteralTypeNode((createNumericLiteral((type).text))); + if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { + return createLiteralTypeNode(createLiteral((type).value)); } if (type.flags & TypeFlags.BooleanLiteral) { return (type).intrinsicName === "true" ? createTrue() : createFalse(); @@ -2891,7 +2888,7 @@ namespace ts { } function literalTypeToString(type: LiteralType) { - return type.flags & TypeFlags.StringLiteral ? `"${escapeString((type).text)}"` : (type).text; + return type.flags & TypeFlags.StringLiteral ? `"${escapeString((type).value)}"` : "" + (type).value; } function getNameOfSymbol(symbol: Symbol): string { @@ -4933,11 +4930,11 @@ namespace ts { return true; } - function createEnumLiteralType(symbol: Symbol, baseType: EnumType, text: string) { + function createEnumLiteralType(symbol: Symbol, baseType: EnumType, value: number) { const type = createType(TypeFlags.EnumLiteral); type.symbol = symbol; type.baseType = baseType; - type.text = text; + type.value = value; return type; } @@ -4956,7 +4953,7 @@ namespace ts { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); if (!memberTypes[value]) { - const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value); + const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, value); memberTypeList.push(memberType); } } @@ -5510,7 +5507,7 @@ namespace ts { // If the current iteration type constituent is a string literal type, create a property. // Otherwise, for type string create a string index signature. if (t.flags & TypeFlags.StringLiteral) { - const propName = (t).text; + const propName = (t).value; const modifiersProp = getPropertyOfType(modifiersType, propName); const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional); const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName); @@ -7216,7 +7213,7 @@ namespace ts { function getLiteralTypeFromPropertyName(prop: Symbol) { return getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || startsWith(prop.name, "__@") ? neverType : - getLiteralTypeForText(TypeFlags.StringLiteral, unescapeIdentifier(prop.name)); + getLiteralType(unescapeIdentifier(prop.name)); } function getLiteralTypeFromPropertyNames(type: Type) { @@ -7253,7 +7250,7 @@ namespace ts { function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) { const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; const propName = indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) ? - (indexType).text : + "" + (indexType).value : accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? getPropertyNameForKnownSymbolName(((accessExpression.argumentExpression).name).text) : undefined; @@ -7301,7 +7298,7 @@ namespace ts { if (accessNode) { const indexNode = accessNode.kind === SyntaxKind.ElementAccessExpression ? (accessNode).argumentExpression : (accessNode).indexType; if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { - error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType).text, typeToString(objectType)); + error(indexNode, Diagnostics.Property_0_does_not_exist_on_type_1, "" + (indexType).value, typeToString(objectType)); } else if (indexType.flags & (TypeFlags.String | TypeFlags.Number)) { error(indexNode, Diagnostics.Type_0_has_no_matching_index_signature_for_type_1, typeToString(objectType), typeToString(indexType)); @@ -7512,16 +7509,16 @@ namespace ts { return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent)); } - function createLiteralType(flags: TypeFlags, text: string) { + function createLiteralType(flags: TypeFlags, value: string | number) { const type = createType(flags); - type.text = text; + type.value = value; return type; } function getFreshTypeOfLiteralType(type: Type) { if (type.flags & TypeFlags.StringOrNumberLiteral && !(type.flags & TypeFlags.FreshLiteral)) { if (!(type).freshType) { - const freshType = createLiteralType(type.flags | TypeFlags.FreshLiteral, (type).text); + const freshType = createLiteralType(type.flags | TypeFlags.FreshLiteral, (type).value); freshType.regularType = type; (type).freshType = freshType; } @@ -7534,11 +7531,12 @@ namespace ts { return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (type).regularType : type; } - function getLiteralTypeForText(flags: TypeFlags, text: string) { - const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; + function getLiteralType(value: string | number) { + const map = typeof value === "number" ? numericLiteralTypes : stringLiteralTypes; + const text = "" + value; let type = map.get(text); if (!type) { - map.set(text, type = createLiteralType(flags, text)); + map.set(text, type = createLiteralType(typeof value === "number" ? TypeFlags.NumberLiteral : TypeFlags.StringLiteral, value)); } return type; } @@ -8434,7 +8432,7 @@ namespace ts { if ((source.flags & TypeFlags.Number | source.flags & TypeFlags.NumberLiteral) && target.flags & TypeFlags.EnumLike) return true; if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral && - (source).text === (target).text && + (source).value === (target).value && isEnumTypeRelatedTo((source).baseType, (target).baseType, errorReporter)) { return true; } @@ -9713,8 +9711,8 @@ namespace ts { // no flags for all other types (including non-falsy literal types). function getFalsyFlags(type: Type): TypeFlags { return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((type).types) : - type.flags & TypeFlags.StringLiteral ? (type).text === "" ? TypeFlags.StringLiteral : 0 : - type.flags & TypeFlags.NumberLiteral ? (type).text === "0" ? TypeFlags.NumberLiteral : 0 : + type.flags & TypeFlags.StringLiteral ? (type).value === "" ? TypeFlags.StringLiteral : 0 : + type.flags & TypeFlags.NumberLiteral ? (type).value === 0 ? TypeFlags.NumberLiteral : 0 : type.flags & TypeFlags.BooleanLiteral ? type === falseType ? TypeFlags.BooleanLiteral : 0 : type.flags & TypeFlags.PossiblyFalsy; } @@ -10595,14 +10593,14 @@ namespace ts { } if (flags & TypeFlags.StringLiteral) { return strictNullChecks ? - (type).text === "" ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts : - (type).text === "" ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts; + (type).value === "" ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts : + (type).value === "" ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts; } if (flags & (TypeFlags.Number | TypeFlags.Enum)) { return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts; } if (flags & (TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { - const isZero = (type).text === "0"; + const isZero = (type).value === 0; return strictNullChecks ? isZero ? TypeFacts.ZeroStrictFacts : TypeFacts.NonZeroStrictFacts : isZero ? TypeFacts.ZeroFacts : TypeFacts.NonZeroFacts; @@ -13616,7 +13614,7 @@ namespace ts { // Hello World const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements); if (intrinsicElementsType !== unknownType) { - const stringLiteralTypeName = (elementType).text; + const stringLiteralTypeName = (elementType).value; const intrinsicProp = getPropertyOfType(intrinsicElementsType, stringLiteralTypeName); if (intrinsicProp) { return getTypeOfSymbol(intrinsicProp); @@ -14861,7 +14859,7 @@ namespace ts { case SyntaxKind.Identifier: case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: - return getLiteralTypeForText(TypeFlags.StringLiteral, (element.name).text); + return getLiteralType((element.name).text); case SyntaxKind.ComputedPropertyName: const nameType = checkComputedPropertyName(element.name); @@ -16345,7 +16343,7 @@ namespace ts { return silentNeverType; } if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral) { - return getFreshTypeOfLiteralType(getLiteralTypeForText(TypeFlags.NumberLiteral, "" + -(node.operand).text)); + return getFreshTypeOfLiteralType(getLiteralType(-(node.operand).text)); } switch (node.operator) { case SyntaxKind.PlusToken: @@ -17021,9 +17019,9 @@ namespace ts { } switch (node.kind) { case SyntaxKind.StringLiteral: - return getFreshTypeOfLiteralType(getLiteralTypeForText(TypeFlags.StringLiteral, (node).text)); + return getFreshTypeOfLiteralType(getLiteralType((node).text)); case SyntaxKind.NumericLiteral: - return getFreshTypeOfLiteralType(getLiteralTypeForText(TypeFlags.NumberLiteral, (node).text)); + return getFreshTypeOfLiteralType(getLiteralType(+(node).text)); case SyntaxKind.TrueKeyword: return trueType; case SyntaxKind.FalseKeyword: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2389ba80753..c259366542b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3036,11 +3036,19 @@ namespace ts { // String literal types (TypeFlags.StringLiteral) // Numeric literal types (TypeFlags.NumberLiteral) export interface LiteralType extends Type { - text: string; // Text of literal + value: string | number; // Value of literal freshType?: LiteralType; // Fresh version of type regularType?: LiteralType; // Regular version of type } + export interface StringLiteralType extends LiteralType { + value: string; + } + + export interface NumberLiteralType extends LiteralType { + value: number; + } + // Enum types (TypeFlags.Enum) export interface EnumType extends Type { memberTypes: EnumLiteralType[]; diff --git a/src/services/completions.ts b/src/services/completions.ts index 6ac3762b4c7..93f690c90f2 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -272,7 +272,7 @@ namespace ts.Completions { } else if (type.flags & TypeFlags.StringLiteral) { result.push({ - name: (type).text, + name: (type).value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.variableElement, sortText: "0"