Numeric and boolean literal types

This commit is contained in:
Anders Hejlsberg 2016-06-18 07:21:23 -07:00
parent c7dec0ea68
commit 89596cb73b
7 changed files with 170 additions and 115 deletions

View file

@ -114,6 +114,8 @@ namespace ts {
const stringType = createIntrinsicType(TypeFlags.String, "string"); const stringType = createIntrinsicType(TypeFlags.String, "string");
const numberType = createIntrinsicType(TypeFlags.Number, "number"); const numberType = createIntrinsicType(TypeFlags.Number, "number");
const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean"); 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 esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
const voidType = createIntrinsicType(TypeFlags.Void, "void"); const voidType = createIntrinsicType(TypeFlags.Void, "void");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
@ -134,8 +136,8 @@ namespace ts {
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); 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 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, /*hasStringLiterals*/ false); const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
@ -195,7 +197,8 @@ namespace ts {
const tupleTypes: Map<TupleType> = {}; const tupleTypes: Map<TupleType> = {};
const unionTypes: Map<UnionType> = {}; const unionTypes: Map<UnionType> = {};
const intersectionTypes: Map<IntersectionType> = {}; const intersectionTypes: Map<IntersectionType> = {};
const stringLiteralTypes: Map<StringLiteralType> = {}; const stringLiteralTypes: Map<LiteralType> = {};
const numericLiteralTypes: Map<LiteralType> = {};
const resolutionTargets: TypeSystemEntity[] = []; const resolutionTargets: TypeSystemEntity[] = [];
const resolutionResults: boolean[] = []; const resolutionResults: boolean[] = [];
@ -2060,7 +2063,10 @@ namespace ts {
writeAnonymousType(<ObjectType>type, flags); writeAnonymousType(<ObjectType>type, flags);
} }
else if (type.flags & TypeFlags.StringLiteral) { else if (type.flags & TypeFlags.StringLiteral) {
writer.writeStringLiteral(`"${escapeString((<StringLiteralType>type).text)}"`); writer.writeStringLiteral(`"${escapeString((<LiteralType>type).text)}"`);
}
else if (type.flags & TypeFlags.NumberLiteral) {
writer.writeStringLiteral((<LiteralType>type).text);
} }
else { else {
// Should never get here // Should never get here
@ -3718,7 +3724,7 @@ namespace ts {
case SyntaxKind.UndefinedKeyword: case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword: case SyntaxKind.NullKeyword:
case SyntaxKind.NeverKeyword: case SyntaxKind.NeverKeyword:
case SyntaxKind.StringLiteralType: case SyntaxKind.LiteralType:
return true; return true;
case SyntaxKind.ArrayType: case SyntaxKind.ArrayType:
return isIndependentType((<ArrayTypeNode>node).elementType); return isIndependentType((<ArrayTypeNode>node).elementType);
@ -3863,7 +3869,7 @@ namespace ts {
} }
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisType: Type, parameters: Symbol[], 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); const sig = new Signature(checker);
sig.declaration = declaration; sig.declaration = declaration;
sig.typeParameters = typeParameters; sig.typeParameters = typeParameters;
@ -3873,20 +3879,20 @@ namespace ts {
sig.typePredicate = typePredicate; sig.typePredicate = typePredicate;
sig.minArgumentCount = minArgumentCount; sig.minArgumentCount = minArgumentCount;
sig.hasRestParameter = hasRestParameter; sig.hasRestParameter = hasRestParameter;
sig.hasStringLiterals = hasStringLiterals; sig.hasLiteralTypes = hasLiteralTypes;
return sig; return sig;
} }
function cloneSignature(sig: Signature): Signature { function cloneSignature(sig: Signature): Signature {
return createSignature(sig.declaration, sig.typeParameters, sig.thisType, sig.parameters, sig.resolvedReturnType, 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[] { function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseConstructorType = getBaseConstructorTypeOfClass(classType);
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
if (baseSignatures.length === 0) { 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 baseTypeNode = getBaseTypeNodeOfClass(classType);
const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode);
@ -4184,7 +4190,7 @@ namespace ts {
else if (type.flags & TypeFlags.NumberLike) { else if (type.flags & TypeFlags.NumberLike) {
type = globalNumberType; type = globalNumberType;
} }
else if (type.flags & TypeFlags.Boolean) { else if (type.flags & TypeFlags.BooleanLike) {
type = globalBooleanType; type = globalBooleanType;
} }
else if (type.flags & TypeFlags.ESSymbol) { else if (type.flags & TypeFlags.ESSymbol) {
@ -4426,7 +4432,7 @@ namespace ts {
const links = getNodeLinks(declaration); const links = getNodeLinks(declaration);
if (!links.resolvedSignature) { if (!links.resolvedSignature) {
const parameters: Symbol[] = []; const parameters: Symbol[] = [];
let hasStringLiterals = false; let hasLiteralTypes = false;
let minArgumentCount = -1; let minArgumentCount = -1;
let thisType: Type = undefined; let thisType: Type = undefined;
let hasThisParameter: boolean; let hasThisParameter: boolean;
@ -4452,8 +4458,8 @@ namespace ts {
parameters.push(paramSymbol); parameters.push(paramSymbol);
} }
if (param.type && param.type.kind === SyntaxKind.StringLiteralType) { if (param.type && param.type.kind === SyntaxKind.LiteralType) {
hasStringLiterals = true; hasLiteralTypes = true;
} }
if (param.initializer || param.questionToken || param.dotDotDotToken) { if (param.initializer || param.questionToken || param.dotDotDotToken) {
@ -4494,7 +4500,7 @@ namespace ts {
createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) :
undefined; 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; return links.resolvedSignature;
} }
@ -5182,19 +5188,53 @@ namespace ts {
return links.resolvedType; return links.resolvedType;
} }
function getStringLiteralTypeForText(text: string): StringLiteralType { function getStringLiteralTypeForText(text: string): LiteralType {
if (hasProperty(stringLiteralTypes, text)) { if (hasProperty(stringLiteralTypes, text)) {
return stringLiteralTypes[text]; return stringLiteralTypes[text];
} }
const type = stringLiteralTypes[text] = <StringLiteralType>createType(TypeFlags.StringLiteral); const type = stringLiteralTypes[text] = <LiteralType>createType(TypeFlags.StringLiteral);
type.text = text; type.text = text;
return type; return type;
} }
function getTypeFromStringLiteralTypeNode(node: StringLiteralTypeNode): Type { function createLiteralType(flags: TypeFlags, text: string) {
const type = <LiteralType>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, (<LiteralExpression>node).text);
case SyntaxKind.NumericLiteral:
return getLiteralTypeForText(TypeFlags.NumberLiteral, (<LiteralExpression>node).text);
case SyntaxKind.TrueKeyword:
return trueType;
case SyntaxKind.FalseKeyword:
return falseType;
case SyntaxKind.PrefixUnaryExpression:
if ((<PrefixUnaryExpression>node).operator === SyntaxKind.MinusToken &&
(<PrefixUnaryExpression>node).operand.kind === SyntaxKind.NumericLiteral) {
return getLiteralTypeForText(TypeFlags.NumberLiteral, "" + -(<LiteralExpression>(<PrefixUnaryExpression>node).operand).text);
}
}
return undefined;
}
function getTypeOfLiteralOrExpression(node: Expression): Type {
return getTypeFromLiteralExpression(node) || checkExpression(node);
}
function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type {
const links = getNodeLinks(node); const links = getNodeLinks(node);
if (!links.resolvedType) { if (!links.resolvedType) {
links.resolvedType = getStringLiteralTypeForText(unescapeIdentifier(node.text)); links.resolvedType = getTypeFromLiteralExpression(node.literal);
} }
return links.resolvedType; return links.resolvedType;
} }
@ -5263,8 +5303,8 @@ namespace ts {
case SyntaxKind.ThisType: case SyntaxKind.ThisType:
case SyntaxKind.ThisKeyword: case SyntaxKind.ThisKeyword:
return getTypeFromThisTypeNode(node); return getTypeFromThisTypeNode(node);
case SyntaxKind.StringLiteralType: case SyntaxKind.LiteralType:
return getTypeFromStringLiteralTypeNode(<StringLiteralTypeNode>node); return getTypeFromLiteralTypeNode(<LiteralTypeNode>node);
case SyntaxKind.TypeReference: case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference: case SyntaxKind.JSDocTypeReference:
return getTypeFromTypeReference(<TypeReferenceNode>node); return getTypeFromTypeReference(<TypeReferenceNode>node);
@ -5431,7 +5471,7 @@ namespace ts {
instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateList(signature.parameters, mapper, instantiateSymbol),
instantiateType(signature.resolvedReturnType, mapper), instantiateType(signature.resolvedReturnType, mapper),
freshTypePredicate, freshTypePredicate,
signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); signature.minArgumentCount, signature.hasRestParameter, signature.hasLiteralTypes);
result.target = signature; result.target = signature;
result.mapper = mapper; result.mapper = mapper;
return result; return result;
@ -5918,18 +5958,18 @@ namespace ts {
if (source.flags & TypeFlags.Null) { if (source.flags & TypeFlags.Null) {
if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True; 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 (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) {
if (result = enumRelatedTo(source, target, reportErrors)) { if (result = enumRelatedTo(source, target, reportErrors)) {
return result; 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 (relation === assignableRelation || relation === comparableRelation) {
if (source.flags & TypeFlags.Any) return Ternary.True; if (source.flags & TypeFlags.Any) return Ternary.True;
if (source === numberType && target.flags & TypeFlags.Enum) 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; return Ternary.True;
} }
} }
@ -6834,9 +6874,9 @@ namespace ts {
return !!getPropertyOfType(type, "0"); return !!getPropertyOfType(type, "0");
} }
function isStringLiteralUnionType(type: Type): boolean { function isLiteralUnionType(type: Type): boolean {
return type.flags & TypeFlags.StringLiteral ? true : return type.flags & TypeFlags.Literal ? true :
type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isStringLiteralUnionType) : type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isLiteralUnionType) :
false; false;
} }
@ -7528,7 +7568,7 @@ namespace ts {
if (flags & TypeFlags.NumberLike) { if (flags & TypeFlags.NumberLike) {
return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts; return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts;
} }
if (flags & TypeFlags.Boolean) { if (flags & TypeFlags.BooleanLike) {
return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts; return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts;
} }
if (flags & TypeFlags.ObjectType) { if (flags & TypeFlags.ObjectType) {
@ -7703,11 +7743,7 @@ namespace ts {
} }
function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
if (clause.kind === SyntaxKind.CaseClause) { return clause.kind === SyntaxKind.CaseClause ? getTypeOfLiteralOrExpression((<CaseClause>clause).expression) : undefined;
const expr = (<CaseClause>clause).expression;
return expr.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((<StringLiteral>expr).text) : checkExpression(expr);
}
return undefined;
} }
function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] { function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] {
@ -8021,11 +8057,11 @@ namespace ts {
} }
const propName = propAccess.name.text; const propName = propAccess.name.text;
const propType = getTypeOfPropertyOfType(type, propName); const propType = getTypeOfPropertyOfType(type, propName);
if (!propType || !isStringLiteralUnionType(propType)) { if (!propType || !isLiteralUnionType(propType)) {
return type; return type;
} }
const discriminantType = value.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((<StringLiteral>value).text) : checkExpression(value); const discriminantType = getTypeOfLiteralOrExpression(value);
if (!isStringLiteralUnionType(discriminantType)) { if (!isLiteralUnionType(discriminantType)) {
return type; return type;
} }
if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
@ -8047,7 +8083,7 @@ namespace ts {
} }
const propName = (<PropertyAccessExpression>switchStatement.expression).name.text; const propName = (<PropertyAccessExpression>switchStatement.expression).name.text;
const propType = getTypeOfPropertyOfType(type, propName); const propType = getTypeOfPropertyOfType(type, propName);
if (!propType || !isStringLiteralUnionType(propType)) { if (!propType || !isLiteralUnionType(propType)) {
return type; return type;
} }
const switchTypes = getSwitchClauseTypes(switchStatement); const switchTypes = getSwitchClauseTypes(switchStatement);
@ -10446,7 +10482,7 @@ namespace ts {
// specialized signatures always need to be placed before non-specialized signatures regardless // specialized signatures always need to be placed before non-specialized signatures regardless
// of the cutoff position; see GH#1133 // of the cutoff position; see GH#1133
if (signature.hasStringLiterals) { if (signature.hasLiteralTypes) {
specializedIndex++; specializedIndex++;
spliceIndex = specializedIndex; spliceIndex = specializedIndex;
// The cutoff index always needs to be greater than or equal to the specialized signature index // 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. // for the argument. In that case, we should check the argument.
if (argType === undefined) { if (argType === undefined) {
argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors
? getStringLiteralTypeForText((<StringLiteral>arg).text) ? getTypeFromLiteralExpression(arg)
: checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); : checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
} }
@ -11877,7 +11913,7 @@ namespace ts {
} }
const propName = (<PropertyAccessExpression>expr).name.text; const propName = (<PropertyAccessExpression>expr).name.text;
const propType = getTypeOfPropertyOfType(type, propName); const propType = getTypeOfPropertyOfType(type, propName);
if (!propType || !isStringLiteralUnionType(propType)) { if (!propType || !isLiteralUnionType(propType)) {
return false; return false;
} }
const switchTypes = getSwitchClauseTypes(node); const switchTypes = getSwitchClauseTypes(node);
@ -12217,6 +12253,9 @@ namespace ts {
function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type {
const operandType = checkExpression(node.operand); const operandType = checkExpression(node.operand);
if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral && hasLiteralContextualType(node)) {
return getTypeFromLiteralExpression(node);
}
switch (node.operator) { switch (node.operator) {
case SyntaxKind.PlusToken: case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken: case SyntaxKind.MinusToken:
@ -12520,8 +12559,8 @@ namespace ts {
let suggestedOperator: SyntaxKind; let suggestedOperator: SyntaxKind;
// if a user tries to apply a bitwise operator to 2 boolean operands // if a user tries to apply a bitwise operator to 2 boolean operands
// try and return them a helpful suggestion // try and return them a helpful suggestion
if ((leftType.flags & TypeFlags.Boolean) && if ((leftType.flags & TypeFlags.BooleanLike) &&
(rightType.flags & TypeFlags.Boolean) && (rightType.flags & TypeFlags.BooleanLike) &&
(suggestedOperator = getSuggestedBooleanOperator(operatorToken.kind)) !== undefined) { (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)); 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]); return getUnionType([type1, type2]);
} }
function checkStringLiteralExpression(node: StringLiteral): Type { function hasLiteralContextualType(node: Expression) {
const contextualType = getContextualType(node); return isLiteralUnionType(getContextualType(node) || unknownType);
if (contextualType && isStringLiteralUnionType(contextualType)) { }
return getStringLiteralTypeForText(node.text);
}
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(<LiteralExpression>node);
}
return hasLiteralContextualType(node) ? getTypeFromLiteralExpression(node) : type;
} }
function checkTemplateExpression(node: TemplateExpression): Type { function checkTemplateExpression(node: TemplateExpression): Type {
@ -12855,12 +12899,6 @@ namespace ts {
return type; return type;
} }
function checkNumericLiteral(node: LiteralExpression): Type {
// Grammar checking
checkGrammarNumericLiteral(node);
return numberType;
}
function checkExpressionWorker(node: Expression, contextualMapper: TypeMapper): Type { function checkExpressionWorker(node: Expression, contextualMapper: TypeMapper): Type {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.Identifier: case SyntaxKind.Identifier:
@ -12871,15 +12909,13 @@ namespace ts {
return checkSuperExpression(node); return checkSuperExpression(node);
case SyntaxKind.NullKeyword: case SyntaxKind.NullKeyword:
return nullWideningType; return nullWideningType;
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword: case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword: case SyntaxKind.FalseKeyword:
return booleanType; return checkLiteralExpression(node);
case SyntaxKind.NumericLiteral:
return checkNumericLiteral(<LiteralExpression>node);
case SyntaxKind.TemplateExpression: case SyntaxKind.TemplateExpression:
return checkTemplateExpression(<TemplateExpression>node); return checkTemplateExpression(<TemplateExpression>node);
case SyntaxKind.StringLiteral:
return checkStringLiteralExpression(<StringLiteral>node);
case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral:
return stringType; return stringType;
case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.RegularExpressionLiteral:
@ -17605,7 +17641,7 @@ namespace ts {
else if (isTypeOfKind(type, TypeFlags.Void)) { else if (isTypeOfKind(type, TypeFlags.Void)) {
return TypeReferenceSerializationKind.VoidType; return TypeReferenceSerializationKind.VoidType;
} }
else if (isTypeOfKind(type, TypeFlags.Boolean)) { else if (isTypeOfKind(type, TypeFlags.BooleanLike)) {
return TypeReferenceSerializationKind.BooleanType; return TypeReferenceSerializationKind.BooleanType;
} }
else if (isTypeOfKind(type, TypeFlags.NumberLike)) { else if (isTypeOfKind(type, TypeFlags.NumberLike)) {

View file

@ -397,7 +397,7 @@ namespace ts {
case SyntaxKind.NullKeyword: case SyntaxKind.NullKeyword:
case SyntaxKind.NeverKeyword: case SyntaxKind.NeverKeyword:
case SyntaxKind.ThisType: case SyntaxKind.ThisType:
case SyntaxKind.StringLiteralType: case SyntaxKind.LiteralType:
return writeTextOfNode(currentText, type); return writeTextOfNode(currentText, type);
case SyntaxKind.ExpressionWithTypeArguments: case SyntaxKind.ExpressionWithTypeArguments:
return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>type); return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>type);

View file

@ -6009,7 +6009,7 @@ const _super = (function (geti, seti) {
return; return;
case SyntaxKind.StringKeyword: case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteralType: case SyntaxKind.LiteralType:
write("String"); write("String");
return; return;

View file

@ -128,6 +128,8 @@ namespace ts {
return visitNodes(cbNodes, (<UnionOrIntersectionTypeNode>node).types); return visitNodes(cbNodes, (<UnionOrIntersectionTypeNode>node).types);
case SyntaxKind.ParenthesizedType: case SyntaxKind.ParenthesizedType:
return visitNode(cbNode, (<ParenthesizedTypeNode>node).type); return visitNode(cbNode, (<ParenthesizedTypeNode>node).type);
case SyntaxKind.LiteralType:
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern: case SyntaxKind.ArrayBindingPattern:
return visitNodes(cbNodes, (<BindingPattern>node).elements); return visitNodes(cbNodes, (<BindingPattern>node).elements);
@ -1918,10 +1920,6 @@ namespace ts {
return finishNode(span); return finishNode(span);
} }
function parseStringLiteralTypeNode(): StringLiteralTypeNode {
return <StringLiteralTypeNode>parseLiteralLikeNode(SyntaxKind.StringLiteralType, /*internName*/ true);
}
function parseLiteralNode(internName?: boolean): LiteralExpression { function parseLiteralNode(internName?: boolean): LiteralExpression {
return <LiteralExpression>parseLiteralLikeNode(token, internName); return <LiteralExpression>parseLiteralLikeNode(token, internName);
} }
@ -2387,6 +2385,17 @@ namespace ts {
return token === SyntaxKind.DotToken ? undefined : node; return token === SyntaxKind.DotToken ? undefined : node;
} }
function parseLiteralTypeNode(): LiteralTypeNode {
const node = <LiteralTypeNode>createNode(SyntaxKind.LiteralType);
node.literal = parseSimpleUnaryExpression();
finishNode(node);
return node;
}
function nextTokenIsNumericLiteral() {
return nextToken() === SyntaxKind.NumericLiteral;
}
function parseNonArrayType(): TypeNode { function parseNonArrayType(): TypeNode {
switch (token) { switch (token) {
case SyntaxKind.AnyKeyword: case SyntaxKind.AnyKeyword:
@ -2400,7 +2409,12 @@ namespace ts {
const node = tryParse(parseKeywordAndNoDot); const node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReference(); return node || parseTypeReference();
case SyntaxKind.StringLiteral: 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.VoidKeyword:
case SyntaxKind.NullKeyword: case SyntaxKind.NullKeyword:
return parseTokenNode<TypeNode>(); return parseTokenNode<TypeNode>();
@ -2444,7 +2458,12 @@ namespace ts {
case SyntaxKind.LessThanToken: case SyntaxKind.LessThanToken:
case SyntaxKind.NewKeyword: case SyntaxKind.NewKeyword:
case SyntaxKind.StringLiteral: case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return true; return true;
case SyntaxKind.MinusToken:
return lookAhead(nextTokenIsNumericLiteral);
case SyntaxKind.OpenParenToken: case SyntaxKind.OpenParenToken:
// Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier, // 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. // or something that starts a type. We don't want to consider things like '(1)' a type.

View file

@ -210,7 +210,7 @@ namespace ts {
IntersectionType, IntersectionType,
ParenthesizedType, ParenthesizedType,
ThisType, ThisType,
StringLiteralType, LiteralType,
// Binding patterns // Binding patterns
ObjectBindingPattern, ObjectBindingPattern,
ArrayBindingPattern, ArrayBindingPattern,
@ -361,7 +361,7 @@ namespace ts {
FirstFutureReservedWord = ImplementsKeyword, FirstFutureReservedWord = ImplementsKeyword,
LastFutureReservedWord = YieldKeyword, LastFutureReservedWord = YieldKeyword,
FirstTypeNode = TypePredicate, FirstTypeNode = TypePredicate,
LastTypeNode = StringLiteralType, LastTypeNode = LiteralType,
FirstPunctuation = OpenBraceToken, FirstPunctuation = OpenBraceToken,
LastPunctuation = CaretEqualsToken, LastPunctuation = CaretEqualsToken,
FirstToken = Unknown, FirstToken = Unknown,
@ -790,8 +790,9 @@ namespace ts {
} }
// @kind(SyntaxKind.StringLiteralType) // @kind(SyntaxKind.StringLiteralType)
export interface StringLiteralTypeNode extends LiteralLikeNode, TypeNode { export interface LiteralTypeNode extends TypeNode {
_stringLiteralTypeBrand: any; _stringLiteralTypeBrand: any;
literal: Expression;
} }
// @kind(SyntaxKind.StringLiteral) // @kind(SyntaxKind.StringLiteral)
@ -2197,50 +2198,54 @@ namespace ts {
} }
export const enum TypeFlags { export const enum TypeFlags {
Any = 0x00000001, Any = 1 << 0,
String = 0x00000002, String = 1 << 1,
Number = 0x00000004, Number = 1 << 2,
Boolean = 0x00000008, Boolean = 1 << 3,
Void = 0x00000010, StringLiteral = 1 << 4, // String literal type
Undefined = 0x00000020, NumberLiteral = 1 << 5,
Null = 0x00000040, BooleanLiteral = 1 << 6,
Enum = 0x00000080, // Enum type ESSymbol = 1 << 7, // Type of symbol primitive introduced in ES6
StringLiteral = 0x00000100, // String literal type Void = 1 << 8,
TypeParameter = 0x00000200, // Type parameter Undefined = 1 << 9,
Class = 0x00000400, // Class Null = 1 << 10,
Interface = 0x00000800, // Interface Never = 1 << 11, // Never type
Reference = 0x00001000, // Generic type reference Enum = 1 << 12, // Enum type
Tuple = 0x00002000, // Tuple TypeParameter = 1 << 13, // Type parameter
Union = 0x00004000, // Union (T | U) Class = 1 << 14, // Class
Intersection = 0x00008000, // Intersection (T & U) Interface = 1 << 15, // Interface
Anonymous = 0x00010000, // Anonymous Reference = 1 << 16, // Generic type reference
Instantiated = 0x00020000, // Instantiated anonymous type 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 */ /* @internal */
FromSignature = 0x00040000, // Created for signature assignment check FromSignature = 1 << 22, // Created for signature assignment check
ObjectLiteral = 0x00080000, // Originates in an object literal ObjectLiteral = 1 << 23, // Originates in an object literal
/* @internal */ /* @internal */
FreshObjectLiteral = 0x00100000, // Fresh object literal type FreshObjectLiteral = 1 << 24, // Fresh object literal type
/* @internal */ /* @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 */ /* @internal */
ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type ContainsObjectLiteral = 1 << 26, // Type is or contains object literal type
/* @internal */ /* @internal */
ContainsAnyFunctionType = 0x00800000, // Type is or contains object literal type ContainsAnyFunctionType = 1 << 27, // Type is or contains object literal type
ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6 ThisType = 1 << 28, // This type
ThisType = 0x02000000, // This type ObjectLiteralPatternWithComputedProperties = 1 << 29, // Object literal type implied by binding pattern has computed properties
ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties
Never = 0x08000000, // Never type
/* @internal */ /* @internal */
Nullable = Undefined | Null, Nullable = Undefined | Null,
Literal = StringLiteral | NumberLiteral | BooleanLiteral,
/* @internal */ /* @internal */
Falsy = Void | Undefined | Null, // TODO: Add false, 0, and "" Falsy = Void | Undefined | Null, // TODO: Add false, 0, and ""
/* @internal */ /* @internal */
Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never, Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never,
/* @internal */ /* @internal */
Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum, Primitive = String | Number | Boolean | ESSymbol | Void | Undefined | Null | StringLiteral | Enum,
StringLike = String | StringLiteral, StringLike = String | StringLiteral,
NumberLike = Number | Enum, NumberLike = Number | NumberLiteral | Enum,
BooleanLike = Boolean | BooleanLiteral,
ObjectType = Class | Interface | Reference | Tuple | Anonymous, ObjectType = Class | Interface | Reference | Tuple | Anonymous,
UnionOrIntersection = Union | Intersection, UnionOrIntersection = Union | Intersection,
StructuredType = ObjectType | Union | Intersection, StructuredType = ObjectType | Union | Intersection,
@ -2271,7 +2276,7 @@ namespace ts {
} }
// String literal types (TypeFlags.StringLiteral) // String literal types (TypeFlags.StringLiteral)
export interface StringLiteralType extends Type { export interface LiteralType extends Type {
text: string; // Text of string literal text: string; // Text of string literal
} }
@ -2394,7 +2399,7 @@ namespace ts {
/* @internal */ /* @internal */
hasRestParameter: boolean; // True if last parameter is rest parameter hasRestParameter: boolean; // True if last parameter is rest parameter
/* @internal */ /* @internal */
hasStringLiterals: boolean; // True if specialized hasLiteralTypes: boolean; // True if specialized
/* @internal */ /* @internal */
target?: Signature; // Instantiation target target?: Signature; // Instantiation target
/* @internal */ /* @internal */

View file

@ -783,7 +783,7 @@ namespace ts {
resolvedReturnType: Type; resolvedReturnType: Type;
minArgumentCount: number; minArgumentCount: number;
hasRestParameter: boolean; hasRestParameter: boolean;
hasStringLiterals: boolean; hasLiteralTypes: boolean;
// Undefined is used to indicate the value has not been computed. If, after computing, the // 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. // symbol has no doc comment, then the empty string will be returned.
@ -3633,7 +3633,6 @@ namespace ts {
function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean { function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean {
if (contextToken.kind === SyntaxKind.StringLiteral if (contextToken.kind === SyntaxKind.StringLiteral
|| contextToken.kind === SyntaxKind.StringLiteralType
|| contextToken.kind === SyntaxKind.RegularExpressionLiteral || contextToken.kind === SyntaxKind.RegularExpressionLiteral
|| isTemplateLiteralKind(contextToken.kind)) { || isTemplateLiteralKind(contextToken.kind)) {
const start = contextToken.getStart(); const start = contextToken.getStart();
@ -4298,7 +4297,7 @@ namespace ts {
else { else {
if (type.flags & TypeFlags.StringLiteral) { if (type.flags & TypeFlags.StringLiteral) {
result.push({ result.push({
name: (<StringLiteralType>type).text, name: (<LiteralType>type).text,
kindModifiers: ScriptElementKindModifier.none, kindModifiers: ScriptElementKindModifier.none,
kind: ScriptElementKind.variableElement, kind: ScriptElementKind.variableElement,
sortText: "0" sortText: "0"
@ -6985,7 +6984,6 @@ namespace ts {
case SyntaxKind.PropertyAccessExpression: case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.QualifiedName: case SyntaxKind.QualifiedName:
case SyntaxKind.StringLiteral: case SyntaxKind.StringLiteral:
case SyntaxKind.StringLiteralType:
case SyntaxKind.FalseKeyword: case SyntaxKind.FalseKeyword:
case SyntaxKind.TrueKeyword: case SyntaxKind.TrueKeyword:
case SyntaxKind.NullKeyword: case SyntaxKind.NullKeyword:
@ -7489,7 +7487,7 @@ namespace ts {
else if (tokenKind === SyntaxKind.NumericLiteral) { else if (tokenKind === SyntaxKind.NumericLiteral) {
return ClassificationType.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; return token.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral;
} }
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) { else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
@ -7983,11 +7981,11 @@ namespace ts {
} }
} }
function getStringLiteralTypeForNode(node: StringLiteral | StringLiteralTypeNode, typeChecker: TypeChecker): StringLiteralType { function getStringLiteralTypeForNode(node: StringLiteral | LiteralTypeNode, typeChecker: TypeChecker): LiteralType {
const searchNode = node.parent.kind === SyntaxKind.StringLiteralType ? <StringLiteralTypeNode>node.parent : node; const searchNode = node.parent.kind === SyntaxKind.LiteralType ? <LiteralTypeNode>node.parent : node;
const type = typeChecker.getTypeAtLocation(searchNode); const type = typeChecker.getTypeAtLocation(searchNode);
if (type && type.flags & TypeFlags.StringLiteral) { if (type && type.flags & TypeFlags.StringLiteral) {
return <StringLiteralType>type; return <LiteralType>type;
} }
return undefined; return undefined;
} }
@ -8474,7 +8472,7 @@ namespace ts {
addResult(start, end, classFromKind(token)); addResult(start, end, classFromKind(token));
if (end >= text.length) { 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. // Check to see if we finished up on a multiline string literal.
const tokenText = scanner.getTokenText(); const tokenText = scanner.getTokenText();
if (scanner.isUnterminated()) { if (scanner.isUnterminated()) {
@ -8624,7 +8622,6 @@ namespace ts {
case SyntaxKind.NumericLiteral: case SyntaxKind.NumericLiteral:
return ClassificationType.numericLiteral; return ClassificationType.numericLiteral;
case SyntaxKind.StringLiteral: case SyntaxKind.StringLiteral:
case SyntaxKind.StringLiteralType:
return ClassificationType.stringLiteral; return ClassificationType.stringLiteral;
case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.RegularExpressionLiteral:
return ClassificationType.regularExpressionLiteral; return ClassificationType.regularExpressionLiteral;

View file

@ -428,8 +428,7 @@ namespace ts {
export function isInString(sourceFile: SourceFile, position: number): boolean { export function isInString(sourceFile: SourceFile, position: number): boolean {
const previousToken = findPrecedingToken(position, sourceFile); const previousToken = findPrecedingToken(position, sourceFile);
if (previousToken && if (previousToken && previousToken.kind === SyntaxKind.StringLiteral) {
(previousToken.kind === SyntaxKind.StringLiteral || previousToken.kind === SyntaxKind.StringLiteralType)) {
const start = previousToken.getStart(); const start = previousToken.getStart();
const end = previousToken.getEnd(); const end = previousToken.getEnd();
@ -627,7 +626,6 @@ namespace ts {
export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind): boolean { export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind): boolean {
if (kind === SyntaxKind.StringLiteral if (kind === SyntaxKind.StringLiteral
|| kind === SyntaxKind.StringLiteralType
|| kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.RegularExpressionLiteral
|| isTemplateLiteralKind(kind)) { || isTemplateLiteralKind(kind)) {
return true; return true;