From 89596cb73b946c50b91ba434160bacbda92f3575 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 18 Jun 2016 07:21:23 -0700 Subject: [PATCH] Numeric and boolean literal types --- src/compiler/checker.ts | 156 ++++++++++++++++++----------- src/compiler/declarationEmitter.ts | 2 +- src/compiler/emitter.ts | 2 +- src/compiler/parser.ts | 29 +++++- src/compiler/types.ts | 75 +++++++------- src/services/services.ts | 17 ++-- src/services/utilities.ts | 4 +- 7 files changed, 170 insertions(+), 115 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e53600f2ac..973a1a79cc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -114,6 +114,8 @@ namespace ts { const stringType = createIntrinsicType(TypeFlags.String, "string"); const numberType = createIntrinsicType(TypeFlags.Number, "number"); const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean"); + const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true"); + const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false"); const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); const voidType = createIntrinsicType(TypeFlags.Void, "void"); const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); @@ -134,8 +136,8 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); - const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); + const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); @@ -195,7 +197,8 @@ namespace ts { const tupleTypes: Map = {}; const unionTypes: Map = {}; const intersectionTypes: Map = {}; - const stringLiteralTypes: Map = {}; + const stringLiteralTypes: Map = {}; + const numericLiteralTypes: Map = {}; const resolutionTargets: TypeSystemEntity[] = []; const resolutionResults: boolean[] = []; @@ -2060,7 +2063,10 @@ namespace ts { writeAnonymousType(type, flags); } else if (type.flags & TypeFlags.StringLiteral) { - writer.writeStringLiteral(`"${escapeString((type).text)}"`); + writer.writeStringLiteral(`"${escapeString((type).text)}"`); + } + else if (type.flags & TypeFlags.NumberLiteral) { + writer.writeStringLiteral((type).text); } else { // Should never get here @@ -3718,7 +3724,7 @@ namespace ts { case SyntaxKind.UndefinedKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.NeverKeyword: - case SyntaxKind.StringLiteralType: + case SyntaxKind.LiteralType: return true; case SyntaxKind.ArrayType: return isIndependentType((node).elementType); @@ -3863,7 +3869,7 @@ namespace ts { } function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisType: Type, parameters: Symbol[], - resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { + resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature { const sig = new Signature(checker); sig.declaration = declaration; sig.typeParameters = typeParameters; @@ -3873,20 +3879,20 @@ namespace ts { sig.typePredicate = typePredicate; sig.minArgumentCount = minArgumentCount; sig.hasRestParameter = hasRestParameter; - sig.hasStringLiterals = hasStringLiterals; + sig.hasLiteralTypes = hasLiteralTypes; return sig; } function cloneSignature(sig: Signature): Signature { return createSignature(sig.declaration, sig.typeParameters, sig.thisType, sig.parameters, sig.resolvedReturnType, - sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals); + sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes); } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); if (baseSignatures.length === 0) { - return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; + return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)]; } const baseTypeNode = getBaseTypeNodeOfClass(classType); const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); @@ -4184,7 +4190,7 @@ namespace ts { else if (type.flags & TypeFlags.NumberLike) { type = globalNumberType; } - else if (type.flags & TypeFlags.Boolean) { + else if (type.flags & TypeFlags.BooleanLike) { type = globalBooleanType; } else if (type.flags & TypeFlags.ESSymbol) { @@ -4426,7 +4432,7 @@ namespace ts { const links = getNodeLinks(declaration); if (!links.resolvedSignature) { const parameters: Symbol[] = []; - let hasStringLiterals = false; + let hasLiteralTypes = false; let minArgumentCount = -1; let thisType: Type = undefined; let hasThisParameter: boolean; @@ -4452,8 +4458,8 @@ namespace ts { parameters.push(paramSymbol); } - if (param.type && param.type.kind === SyntaxKind.StringLiteralType) { - hasStringLiterals = true; + if (param.type && param.type.kind === SyntaxKind.LiteralType) { + hasLiteralTypes = true; } if (param.initializer || param.questionToken || param.dotDotDotToken) { @@ -4494,7 +4500,7 @@ namespace ts { createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : undefined; - links.resolvedSignature = createSignature(declaration, typeParameters, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); + links.resolvedSignature = createSignature(declaration, typeParameters, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasLiteralTypes); } return links.resolvedSignature; } @@ -5182,19 +5188,53 @@ namespace ts { return links.resolvedType; } - function getStringLiteralTypeForText(text: string): StringLiteralType { + function getStringLiteralTypeForText(text: string): LiteralType { if (hasProperty(stringLiteralTypes, text)) { return stringLiteralTypes[text]; } - const type = stringLiteralTypes[text] = createType(TypeFlags.StringLiteral); + const type = stringLiteralTypes[text] = createType(TypeFlags.StringLiteral); type.text = text; return type; } - function getTypeFromStringLiteralTypeNode(node: StringLiteralTypeNode): Type { + function createLiteralType(flags: TypeFlags, text: string) { + const type = createType(flags); + type.text = text; + return type; + } + + function getLiteralTypeForText(flags: TypeFlags, text: string) { + const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; + return hasProperty(map, text) ? map[text] : map[text] = createLiteralType(flags, text); + } + + function getTypeFromLiteralExpression(node: Expression): Type { + switch (node.kind) { + case SyntaxKind.StringLiteral: + return getLiteralTypeForText(TypeFlags.StringLiteral, (node).text); + case SyntaxKind.NumericLiteral: + return getLiteralTypeForText(TypeFlags.NumberLiteral, (node).text); + case SyntaxKind.TrueKeyword: + return trueType; + case SyntaxKind.FalseKeyword: + return falseType; + case SyntaxKind.PrefixUnaryExpression: + if ((node).operator === SyntaxKind.MinusToken && + (node).operand.kind === SyntaxKind.NumericLiteral) { + return getLiteralTypeForText(TypeFlags.NumberLiteral, "" + -((node).operand).text); + } + } + return undefined; + } + + function getTypeOfLiteralOrExpression(node: Expression): Type { + return getTypeFromLiteralExpression(node) || checkExpression(node); + } + + function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getStringLiteralTypeForText(unescapeIdentifier(node.text)); + links.resolvedType = getTypeFromLiteralExpression(node.literal); } return links.resolvedType; } @@ -5263,8 +5303,8 @@ namespace ts { case SyntaxKind.ThisType: case SyntaxKind.ThisKeyword: return getTypeFromThisTypeNode(node); - case SyntaxKind.StringLiteralType: - return getTypeFromStringLiteralTypeNode(node); + case SyntaxKind.LiteralType: + return getTypeFromLiteralTypeNode(node); case SyntaxKind.TypeReference: case SyntaxKind.JSDocTypeReference: return getTypeFromTypeReference(node); @@ -5431,7 +5471,7 @@ namespace ts { instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateType(signature.resolvedReturnType, mapper), freshTypePredicate, - signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); + signature.minArgumentCount, signature.hasRestParameter, signature.hasLiteralTypes); result.target = signature; result.mapper = mapper; return result; @@ -5918,18 +5958,18 @@ namespace ts { 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.NumberLike && target === numberType) return Ternary.True; if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) { if (result = enumRelatedTo(source, target, reportErrors)) { return result; } } - if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True; + if (source.flags & TypeFlags.StringLike && target === stringType) return Ternary.True; if (relation === assignableRelation || relation === comparableRelation) { if (source.flags & TypeFlags.Any) return Ternary.True; if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True; } - if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean) { + if (source.flags & TypeFlags.BooleanLike && target.flags & TypeFlags.Boolean) { return Ternary.True; } } @@ -6834,9 +6874,9 @@ namespace ts { return !!getPropertyOfType(type, "0"); } - function isStringLiteralUnionType(type: Type): boolean { - return type.flags & TypeFlags.StringLiteral ? true : - type.flags & TypeFlags.Union ? forEach((type).types, isStringLiteralUnionType) : + function isLiteralUnionType(type: Type): boolean { + return type.flags & TypeFlags.Literal ? true : + type.flags & TypeFlags.Union ? forEach((type).types, isLiteralUnionType) : false; } @@ -7528,7 +7568,7 @@ namespace ts { if (flags & TypeFlags.NumberLike) { return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts; } - if (flags & TypeFlags.Boolean) { + if (flags & TypeFlags.BooleanLike) { return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts; } if (flags & TypeFlags.ObjectType) { @@ -7703,11 +7743,7 @@ namespace ts { } function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { - if (clause.kind === SyntaxKind.CaseClause) { - const expr = (clause).expression; - return expr.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((expr).text) : checkExpression(expr); - } - return undefined; + return clause.kind === SyntaxKind.CaseClause ? getTypeOfLiteralOrExpression((clause).expression) : undefined; } function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] { @@ -8021,11 +8057,11 @@ namespace ts { } const propName = propAccess.name.text; const propType = getTypeOfPropertyOfType(type, propName); - if (!propType || !isStringLiteralUnionType(propType)) { + if (!propType || !isLiteralUnionType(propType)) { return type; } - const discriminantType = value.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((value).text) : checkExpression(value); - if (!isStringLiteralUnionType(discriminantType)) { + const discriminantType = getTypeOfLiteralOrExpression(value); + if (!isLiteralUnionType(discriminantType)) { return type; } if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { @@ -8047,7 +8083,7 @@ namespace ts { } const propName = (switchStatement.expression).name.text; const propType = getTypeOfPropertyOfType(type, propName); - if (!propType || !isStringLiteralUnionType(propType)) { + if (!propType || !isLiteralUnionType(propType)) { return type; } const switchTypes = getSwitchClauseTypes(switchStatement); @@ -10446,7 +10482,7 @@ namespace ts { // specialized signatures always need to be placed before non-specialized signatures regardless // of the cutoff position; see GH#1133 - if (signature.hasStringLiterals) { + if (signature.hasLiteralTypes) { specializedIndex++; spliceIndex = specializedIndex; // The cutoff index always needs to be greater than or equal to the specialized signature index @@ -10705,7 +10741,7 @@ namespace ts { // for the argument. In that case, we should check the argument. if (argType === undefined) { argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors - ? getStringLiteralTypeForText((arg).text) + ? getTypeFromLiteralExpression(arg) : checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); } @@ -11877,7 +11913,7 @@ namespace ts { } const propName = (expr).name.text; const propType = getTypeOfPropertyOfType(type, propName); - if (!propType || !isStringLiteralUnionType(propType)) { + if (!propType || !isLiteralUnionType(propType)) { return false; } const switchTypes = getSwitchClauseTypes(node); @@ -12217,6 +12253,9 @@ namespace ts { function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { const operandType = checkExpression(node.operand); + if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral && hasLiteralContextualType(node)) { + return getTypeFromLiteralExpression(node); + } switch (node.operator) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: @@ -12520,8 +12559,8 @@ namespace ts { let suggestedOperator: SyntaxKind; // if a user tries to apply a bitwise operator to 2 boolean operands // try and return them a helpful suggestion - if ((leftType.flags & TypeFlags.Boolean) && - (rightType.flags & TypeFlags.Boolean) && + if ((leftType.flags & TypeFlags.BooleanLike) && + (rightType.flags & TypeFlags.BooleanLike) && (suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) { error(errorNode || operatorToken, Diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, tokenToString(operatorToken.kind), tokenToString(suggestedOperator)); } @@ -12734,13 +12773,18 @@ namespace ts { return getUnionType([type1, type2]); } - function checkStringLiteralExpression(node: StringLiteral): Type { - const contextualType = getContextualType(node); - if (contextualType && isStringLiteralUnionType(contextualType)) { - return getStringLiteralTypeForText(node.text); - } + function hasLiteralContextualType(node: Expression) { + return isLiteralUnionType(getContextualType(node) || unknownType); + } - return stringType; + function checkLiteralExpression(node: Expression): Type { + const type = node.kind === SyntaxKind.StringLiteral ? stringType : + node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword ? booleanType : + numberType; + if (type === numberType) { + checkGrammarNumericLiteral(node); + } + return hasLiteralContextualType(node) ? getTypeFromLiteralExpression(node) : type; } function checkTemplateExpression(node: TemplateExpression): Type { @@ -12855,12 +12899,6 @@ namespace ts { return type; } - function checkNumericLiteral(node: LiteralExpression): Type { - // Grammar checking - checkGrammarNumericLiteral(node); - return numberType; - } - function checkExpressionWorker(node: Expression, contextualMapper: TypeMapper): Type { switch (node.kind) { case SyntaxKind.Identifier: @@ -12871,15 +12909,13 @@ namespace ts { return checkSuperExpression(node); case SyntaxKind.NullKeyword: return nullWideningType; + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: - return booleanType; - case SyntaxKind.NumericLiteral: - return checkNumericLiteral(node); + return checkLiteralExpression(node); case SyntaxKind.TemplateExpression: return checkTemplateExpression(node); - case SyntaxKind.StringLiteral: - return checkStringLiteralExpression(node); case SyntaxKind.NoSubstitutionTemplateLiteral: return stringType; case SyntaxKind.RegularExpressionLiteral: @@ -17605,7 +17641,7 @@ namespace ts { else if (isTypeOfKind(type, TypeFlags.Void)) { return TypeReferenceSerializationKind.VoidType; } - else if (isTypeOfKind(type, TypeFlags.Boolean)) { + else if (isTypeOfKind(type, TypeFlags.BooleanLike)) { return TypeReferenceSerializationKind.BooleanType; } else if (isTypeOfKind(type, TypeFlags.NumberLike)) { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 2708833a13..d93a8a0aed 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -397,7 +397,7 @@ namespace ts { case SyntaxKind.NullKeyword: case SyntaxKind.NeverKeyword: case SyntaxKind.ThisType: - case SyntaxKind.StringLiteralType: + case SyntaxKind.LiteralType: return writeTextOfNode(currentText, type); case SyntaxKind.ExpressionWithTypeArguments: return emitExpressionWithTypeArguments(type); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 101778e732..057cbecb0a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -6009,7 +6009,7 @@ const _super = (function (geti, seti) { return; case SyntaxKind.StringKeyword: - case SyntaxKind.StringLiteralType: + case SyntaxKind.LiteralType: write("String"); return; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b2f3755ec4..c206b66645 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -128,6 +128,8 @@ namespace ts { return visitNodes(cbNodes, (node).types); case SyntaxKind.ParenthesizedType: return visitNode(cbNode, (node).type); + case SyntaxKind.LiteralType: + return visitNode(cbNode, (node).literal); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: return visitNodes(cbNodes, (node).elements); @@ -1918,10 +1920,6 @@ namespace ts { return finishNode(span); } - function parseStringLiteralTypeNode(): StringLiteralTypeNode { - return parseLiteralLikeNode(SyntaxKind.StringLiteralType, /*internName*/ true); - } - function parseLiteralNode(internName?: boolean): LiteralExpression { return parseLiteralLikeNode(token, internName); } @@ -2387,6 +2385,17 @@ namespace ts { return token === SyntaxKind.DotToken ? undefined : node; } + function parseLiteralTypeNode(): LiteralTypeNode { + const node = createNode(SyntaxKind.LiteralType); + node.literal = parseSimpleUnaryExpression(); + finishNode(node); + return node; + } + + function nextTokenIsNumericLiteral() { + return nextToken() === SyntaxKind.NumericLiteral; + } + function parseNonArrayType(): TypeNode { switch (token) { case SyntaxKind.AnyKeyword: @@ -2400,7 +2409,12 @@ namespace ts { const node = tryParse(parseKeywordAndNoDot); return node || parseTypeReference(); case SyntaxKind.StringLiteral: - return parseStringLiteralTypeNode(); + case SyntaxKind.NumericLiteral: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + return parseLiteralTypeNode(); + case SyntaxKind.MinusToken: + return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode() : parseTypeReference(); case SyntaxKind.VoidKeyword: case SyntaxKind.NullKeyword: return parseTokenNode(); @@ -2444,7 +2458,12 @@ namespace ts { case SyntaxKind.LessThanToken: case SyntaxKind.NewKeyword: case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: return true; + case SyntaxKind.MinusToken: + return lookAhead(nextTokenIsNumericLiteral); case SyntaxKind.OpenParenToken: // Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier, // or something that starts a type. We don't want to consider things like '(1)' a type. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 22861f04c5..04027042c2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -210,7 +210,7 @@ namespace ts { IntersectionType, ParenthesizedType, ThisType, - StringLiteralType, + LiteralType, // Binding patterns ObjectBindingPattern, ArrayBindingPattern, @@ -361,7 +361,7 @@ namespace ts { FirstFutureReservedWord = ImplementsKeyword, LastFutureReservedWord = YieldKeyword, FirstTypeNode = TypePredicate, - LastTypeNode = StringLiteralType, + LastTypeNode = LiteralType, FirstPunctuation = OpenBraceToken, LastPunctuation = CaretEqualsToken, FirstToken = Unknown, @@ -790,8 +790,9 @@ namespace ts { } // @kind(SyntaxKind.StringLiteralType) - export interface StringLiteralTypeNode extends LiteralLikeNode, TypeNode { + export interface LiteralTypeNode extends TypeNode { _stringLiteralTypeBrand: any; + literal: Expression; } // @kind(SyntaxKind.StringLiteral) @@ -2197,50 +2198,54 @@ namespace ts { } export const enum TypeFlags { - Any = 0x00000001, - String = 0x00000002, - Number = 0x00000004, - Boolean = 0x00000008, - Void = 0x00000010, - Undefined = 0x00000020, - Null = 0x00000040, - Enum = 0x00000080, // Enum type - StringLiteral = 0x00000100, // String literal type - TypeParameter = 0x00000200, // Type parameter - Class = 0x00000400, // Class - Interface = 0x00000800, // Interface - Reference = 0x00001000, // Generic type reference - Tuple = 0x00002000, // Tuple - Union = 0x00004000, // Union (T | U) - Intersection = 0x00008000, // Intersection (T & U) - Anonymous = 0x00010000, // Anonymous - Instantiated = 0x00020000, // Instantiated anonymous type + Any = 1 << 0, + String = 1 << 1, + Number = 1 << 2, + Boolean = 1 << 3, + StringLiteral = 1 << 4, // String literal type + NumberLiteral = 1 << 5, + BooleanLiteral = 1 << 6, + ESSymbol = 1 << 7, // Type of symbol primitive introduced in ES6 + Void = 1 << 8, + Undefined = 1 << 9, + Null = 1 << 10, + Never = 1 << 11, // Never type + Enum = 1 << 12, // Enum type + TypeParameter = 1 << 13, // Type parameter + Class = 1 << 14, // Class + Interface = 1 << 15, // Interface + Reference = 1 << 16, // Generic type reference + Tuple = 1 << 17, // Tuple + Union = 1 << 18, // Union (T | U) + Intersection = 1 << 19, // Intersection (T & U) + Anonymous = 1 << 20, // Anonymous + Instantiated = 1 << 21, // Instantiated anonymous type /* @internal */ - FromSignature = 0x00040000, // Created for signature assignment check - ObjectLiteral = 0x00080000, // Originates in an object literal + FromSignature = 1 << 22, // Created for signature assignment check + ObjectLiteral = 1 << 23, // Originates in an object literal /* @internal */ - FreshObjectLiteral = 0x00100000, // Fresh object literal type + FreshObjectLiteral = 1 << 24, // Fresh object literal type /* @internal */ - ContainsWideningType = 0x00200000, // Type is or contains undefined or null widening type + ContainsWideningType = 1 << 25, // Type is or contains undefined or null widening type /* @internal */ - ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type + ContainsObjectLiteral = 1 << 26, // Type is or contains object literal type /* @internal */ - ContainsAnyFunctionType = 0x00800000, // Type is or contains object literal type - ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6 - ThisType = 0x02000000, // This type - ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties - Never = 0x08000000, // Never type + ContainsAnyFunctionType = 1 << 27, // Type is or contains object literal type + ThisType = 1 << 28, // This type + ObjectLiteralPatternWithComputedProperties = 1 << 29, // Object literal type implied by binding pattern has computed properties /* @internal */ Nullable = Undefined | Null, + Literal = StringLiteral | NumberLiteral | BooleanLiteral, /* @internal */ Falsy = Void | Undefined | Null, // TODO: Add false, 0, and "" /* @internal */ - Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never, + Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never, /* @internal */ Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum, StringLike = String | StringLiteral, - NumberLike = Number | Enum, + NumberLike = Number | NumberLiteral | Enum, + BooleanLike = Boolean | BooleanLiteral, ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, @@ -2271,7 +2276,7 @@ namespace ts { } // String literal types (TypeFlags.StringLiteral) - export interface StringLiteralType extends Type { + export interface LiteralType extends Type { text: string; // Text of string literal } @@ -2394,7 +2399,7 @@ namespace ts { /* @internal */ hasRestParameter: boolean; // True if last parameter is rest parameter /* @internal */ - hasStringLiterals: boolean; // True if specialized + hasLiteralTypes: boolean; // True if specialized /* @internal */ target?: Signature; // Instantiation target /* @internal */ diff --git a/src/services/services.ts b/src/services/services.ts index 983ddfbf25..1e205bb393 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -783,7 +783,7 @@ namespace ts { resolvedReturnType: Type; minArgumentCount: number; hasRestParameter: boolean; - hasStringLiterals: boolean; + hasLiteralTypes: boolean; // Undefined is used to indicate the value has not been computed. If, after computing, the // symbol has no doc comment, then the empty string will be returned. @@ -3633,7 +3633,6 @@ namespace ts { function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean { if (contextToken.kind === SyntaxKind.StringLiteral - || contextToken.kind === SyntaxKind.StringLiteralType || contextToken.kind === SyntaxKind.RegularExpressionLiteral || isTemplateLiteralKind(contextToken.kind)) { const start = contextToken.getStart(); @@ -4298,7 +4297,7 @@ namespace ts { else { if (type.flags & TypeFlags.StringLiteral) { result.push({ - name: (type).text, + name: (type).text, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.variableElement, sortText: "0" @@ -6985,7 +6984,6 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: case SyntaxKind.QualifiedName: case SyntaxKind.StringLiteral: - case SyntaxKind.StringLiteralType: case SyntaxKind.FalseKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.NullKeyword: @@ -7489,7 +7487,7 @@ namespace ts { else if (tokenKind === SyntaxKind.NumericLiteral) { return ClassificationType.numericLiteral; } - else if (tokenKind === SyntaxKind.StringLiteral || tokenKind === SyntaxKind.StringLiteralType) { + else if (tokenKind === SyntaxKind.StringLiteral) { return token.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral; } else if (tokenKind === SyntaxKind.RegularExpressionLiteral) { @@ -7983,11 +7981,11 @@ namespace ts { } } - function getStringLiteralTypeForNode(node: StringLiteral | StringLiteralTypeNode, typeChecker: TypeChecker): StringLiteralType { - const searchNode = node.parent.kind === SyntaxKind.StringLiteralType ? node.parent : node; + function getStringLiteralTypeForNode(node: StringLiteral | LiteralTypeNode, typeChecker: TypeChecker): LiteralType { + const searchNode = node.parent.kind === SyntaxKind.LiteralType ? node.parent : node; const type = typeChecker.getTypeAtLocation(searchNode); if (type && type.flags & TypeFlags.StringLiteral) { - return type; + return type; } return undefined; } @@ -8474,7 +8472,7 @@ namespace ts { addResult(start, end, classFromKind(token)); if (end >= text.length) { - if (token === SyntaxKind.StringLiteral || token === SyntaxKind.StringLiteralType) { + if (token === SyntaxKind.StringLiteral) { // Check to see if we finished up on a multiline string literal. const tokenText = scanner.getTokenText(); if (scanner.isUnterminated()) { @@ -8624,7 +8622,6 @@ namespace ts { case SyntaxKind.NumericLiteral: return ClassificationType.numericLiteral; case SyntaxKind.StringLiteral: - case SyntaxKind.StringLiteralType: return ClassificationType.stringLiteral; case SyntaxKind.RegularExpressionLiteral: return ClassificationType.regularExpressionLiteral; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index d49fa39043..ce2b30c839 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -428,8 +428,7 @@ namespace ts { export function isInString(sourceFile: SourceFile, position: number): boolean { const previousToken = findPrecedingToken(position, sourceFile); - if (previousToken && - (previousToken.kind === SyntaxKind.StringLiteral || previousToken.kind === SyntaxKind.StringLiteralType)) { + if (previousToken && previousToken.kind === SyntaxKind.StringLiteral) { const start = previousToken.getStart(); const end = previousToken.getEnd(); @@ -627,7 +626,6 @@ namespace ts { export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind): boolean { if (kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.StringLiteralType || kind === SyntaxKind.RegularExpressionLiteral || isTemplateLiteralKind(kind)) { return true;