Merge pull request #5725 from Microsoft/stringTypesCleanup
Disambiguate string literal types from other string literals.
This commit is contained in:
commit
8909c3a2dd
|
@ -532,7 +532,7 @@ namespace ts {
|
|||
}
|
||||
|
||||
// Because of module/namespace merging, a module's exports are in scope,
|
||||
// yet we never want to treat an export specifier as putting a member in scope.
|
||||
// yet we never want to treat an export specifier as putting a member in scope.
|
||||
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
|
||||
// Two things to note about this:
|
||||
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
|
||||
|
@ -3197,7 +3197,7 @@ namespace ts {
|
|||
case SyntaxKind.BooleanKeyword:
|
||||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.StringLiteralType:
|
||||
return true;
|
||||
case SyntaxKind.ArrayType:
|
||||
return isIndependentType((<ArrayTypeNode>node).elementType);
|
||||
|
@ -3858,7 +3858,7 @@ namespace ts {
|
|||
paramSymbol = resolvedSymbol;
|
||||
}
|
||||
parameters.push(paramSymbol);
|
||||
if (param.type && param.type.kind === SyntaxKind.StringLiteral) {
|
||||
if (param.type && param.type.kind === SyntaxKind.StringLiteralType) {
|
||||
hasStringLiterals = true;
|
||||
}
|
||||
|
||||
|
@ -4527,8 +4527,7 @@ namespace ts {
|
|||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getStringLiteralType(node: StringLiteral): StringLiteralType {
|
||||
const text = node.text;
|
||||
function getStringLiteralType(text: string): StringLiteralType {
|
||||
if (hasProperty(stringLiteralTypes, text)) {
|
||||
return stringLiteralTypes[text];
|
||||
}
|
||||
|
@ -4538,10 +4537,10 @@ namespace ts {
|
|||
return type;
|
||||
}
|
||||
|
||||
function getTypeFromStringLiteral(node: StringLiteral): Type {
|
||||
function getTypeFromStringLiteral(node: StringLiteral | StringLiteralTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = getStringLiteralType(node);
|
||||
links.resolvedType = getStringLiteralType(node.text);
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
@ -4583,8 +4582,8 @@ namespace ts {
|
|||
return voidType;
|
||||
case SyntaxKind.ThisType:
|
||||
return getTypeFromThisTypeNode(node);
|
||||
case SyntaxKind.StringLiteral:
|
||||
return getTypeFromStringLiteral(<StringLiteral>node);
|
||||
case SyntaxKind.StringLiteralType:
|
||||
return getTypeFromStringLiteral(<StringLiteralTypeNode>node);
|
||||
case SyntaxKind.TypeReference:
|
||||
return getTypeFromTypeReference(<TypeReferenceNode>node);
|
||||
case SyntaxKind.TypePredicate:
|
||||
|
@ -8791,7 +8790,7 @@ namespace ts {
|
|||
// for the argument. In that case, we should check the argument.
|
||||
if (argType === undefined) {
|
||||
argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors
|
||||
? getStringLiteralType(<StringLiteral>arg)
|
||||
? getStringLiteralType((<StringLiteral>arg).text)
|
||||
: checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
|
||||
}
|
||||
|
||||
|
@ -8986,7 +8985,7 @@ namespace ts {
|
|||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
return getStringLiteralType(<StringLiteral>element.name);
|
||||
return getStringLiteralType((<Identifier | LiteralExpression>element.name).text);
|
||||
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
const nameType = checkComputedPropertyName(<ComputedPropertyName>element.name);
|
||||
|
@ -10608,7 +10607,8 @@ namespace ts {
|
|||
function checkStringLiteralExpression(node: StringLiteral): Type {
|
||||
const contextualType = getContextualType(node);
|
||||
if (contextualType && contextualTypeIsStringLiteralType(contextualType)) {
|
||||
return getStringLiteralType(node);
|
||||
// TODO (drosen): Consider using getTypeFromStringLiteral instead
|
||||
return getStringLiteralType(node.text);
|
||||
}
|
||||
|
||||
return stringType;
|
||||
|
@ -11442,7 +11442,7 @@ namespace ts {
|
|||
// we can get here in two cases
|
||||
// 1. mixed static and instance class members
|
||||
// 2. something with the same name was defined before the set of overloads that prevents them from merging
|
||||
// here we'll report error only for the first case since for second we should already report error in binder
|
||||
// here we'll report error only for the first case since for second we should already report error in binder
|
||||
if (reportError) {
|
||||
const diagnostic = node.flags & NodeFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static;
|
||||
error(errorNode, diagnostic);
|
||||
|
|
|
@ -357,7 +357,7 @@ namespace ts {
|
|||
case SyntaxKind.SymbolKeyword:
|
||||
case SyntaxKind.VoidKeyword:
|
||||
case SyntaxKind.ThisType:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.StringLiteralType:
|
||||
return writeTextOfNode(currentText, type);
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>type);
|
||||
|
|
|
@ -1276,7 +1276,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
|||
}
|
||||
}
|
||||
|
||||
function isBinaryOrOctalIntegerLiteral(node: LiteralExpression, text: string): boolean {
|
||||
function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string): boolean {
|
||||
if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) {
|
||||
switch (text.charCodeAt(1)) {
|
||||
case CharacterCodes.b:
|
||||
|
@ -1290,7 +1290,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
|||
return false;
|
||||
}
|
||||
|
||||
function emitLiteral(node: LiteralExpression) {
|
||||
function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) {
|
||||
const text = getLiteralText(node);
|
||||
|
||||
if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
|
||||
|
@ -1305,7 +1305,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
|||
}
|
||||
}
|
||||
|
||||
function getLiteralText(node: LiteralExpression) {
|
||||
function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) {
|
||||
// Any template literal or string literal with an extended escape
|
||||
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
|
||||
if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
|
||||
|
@ -1364,7 +1364,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
|||
write(`"${text}"`);
|
||||
}
|
||||
|
||||
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression) => void) {
|
||||
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression | TemplateLiteralFragment) => void) {
|
||||
write("[");
|
||||
if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
|
||||
literalEmitter(<LiteralExpression>node.template);
|
||||
|
@ -5954,7 +5954,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
|||
|
||||
function emitSerializedTypeNode(node: TypeNode) {
|
||||
if (node) {
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VoidKeyword:
|
||||
write("void 0");
|
||||
|
@ -5980,7 +5979,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
|
|||
return;
|
||||
|
||||
case SyntaxKind.StringKeyword:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.StringLiteralType:
|
||||
write("String");
|
||||
return;
|
||||
|
||||
|
|
|
@ -1874,7 +1874,7 @@ namespace ts {
|
|||
function parseTemplateExpression(): TemplateExpression {
|
||||
const template = <TemplateExpression>createNode(SyntaxKind.TemplateExpression);
|
||||
|
||||
template.head = parseLiteralNode();
|
||||
template.head = parseTemplateLiteralFragment();
|
||||
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
|
||||
|
||||
const templateSpans = <NodeArray<TemplateSpan>>[];
|
||||
|
@ -1895,22 +1895,34 @@ namespace ts {
|
|||
const span = <TemplateSpan>createNode(SyntaxKind.TemplateSpan);
|
||||
span.expression = allowInAnd(parseExpression);
|
||||
|
||||
let literal: LiteralExpression;
|
||||
let literal: TemplateLiteralFragment;
|
||||
|
||||
if (token === SyntaxKind.CloseBraceToken) {
|
||||
reScanTemplateToken();
|
||||
literal = parseLiteralNode();
|
||||
literal = parseTemplateLiteralFragment();
|
||||
}
|
||||
else {
|
||||
literal = <LiteralExpression>parseExpectedToken(SyntaxKind.TemplateTail, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken));
|
||||
literal = <TemplateLiteralFragment>parseExpectedToken(SyntaxKind.TemplateTail, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken));
|
||||
}
|
||||
|
||||
span.literal = literal;
|
||||
return finishNode(span);
|
||||
}
|
||||
|
||||
function parseStringLiteralTypeNode(): StringLiteralTypeNode {
|
||||
return <StringLiteralTypeNode>parseLiteralLikeNode(SyntaxKind.StringLiteralType, /*internName*/ true);
|
||||
}
|
||||
|
||||
function parseLiteralNode(internName?: boolean): LiteralExpression {
|
||||
const node = <LiteralExpression>createNode(token);
|
||||
return <LiteralExpression>parseLiteralLikeNode(token, internName);
|
||||
}
|
||||
|
||||
function parseTemplateLiteralFragment(): TemplateLiteralFragment {
|
||||
return <TemplateLiteralFragment>parseLiteralLikeNode(token, /*internName*/ false);
|
||||
}
|
||||
|
||||
function parseLiteralLikeNode(kind: SyntaxKind, internName: boolean): LiteralLikeNode {
|
||||
const node = <LiteralExpression>createNode(kind);
|
||||
const text = scanner.getTokenValue();
|
||||
node.text = internName ? internIdentifier(text) : text;
|
||||
|
||||
|
@ -2397,7 +2409,7 @@ namespace ts {
|
|||
const node = tryParse(parseKeywordAndNoDot);
|
||||
return node || parseTypeReferenceOrTypePredicate();
|
||||
case SyntaxKind.StringLiteral:
|
||||
return <StringLiteral>parseLiteralNode(/*internName*/ true);
|
||||
return parseStringLiteralTypeNode();
|
||||
case SyntaxKind.VoidKeyword:
|
||||
return parseTokenNode<TypeNode>();
|
||||
case SyntaxKind.ThisKeyword:
|
||||
|
|
|
@ -205,6 +205,7 @@ namespace ts {
|
|||
IntersectionType,
|
||||
ParenthesizedType,
|
||||
ThisType,
|
||||
StringLiteralType,
|
||||
// Binding patterns
|
||||
ObjectBindingPattern,
|
||||
ArrayBindingPattern,
|
||||
|
@ -350,7 +351,7 @@ namespace ts {
|
|||
FirstFutureReservedWord = ImplementsKeyword,
|
||||
LastFutureReservedWord = YieldKeyword,
|
||||
FirstTypeNode = TypePredicate,
|
||||
LastTypeNode = ThisType,
|
||||
LastTypeNode = StringLiteralType,
|
||||
FirstPunctuation = OpenBraceToken,
|
||||
LastPunctuation = CaretEqualsToken,
|
||||
FirstToken = Unknown,
|
||||
|
@ -790,10 +791,13 @@ namespace ts {
|
|||
type: TypeNode;
|
||||
}
|
||||
|
||||
// Note that a StringLiteral AST node is both an Expression and a TypeNode. The latter is
|
||||
// because string literals can appear in type annotations as well.
|
||||
// @kind(SyntaxKind.StringLiteralType)
|
||||
export interface StringLiteralTypeNode extends LiteralLikeNode, TypeNode {
|
||||
_stringLiteralTypeBrand: any;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.StringLiteral)
|
||||
export interface StringLiteral extends LiteralExpression, TypeNode {
|
||||
export interface StringLiteral extends LiteralExpression {
|
||||
_stringLiteralBrand: any;
|
||||
}
|
||||
|
||||
|
@ -911,24 +915,32 @@ namespace ts {
|
|||
body: ConciseBody;
|
||||
}
|
||||
|
||||
export interface LiteralLikeNode extends Node {
|
||||
text: string;
|
||||
isUnterminated?: boolean;
|
||||
hasExtendedUnicodeEscape?: boolean;
|
||||
}
|
||||
|
||||
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
|
||||
// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters.
|
||||
// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
|
||||
// @kind(SyntaxKind.NumericLiteral)
|
||||
// @kind(SyntaxKind.RegularExpressionLiteral)
|
||||
// @kind(SyntaxKind.NoSubstitutionTemplateLiteral)
|
||||
export interface LiteralExpression extends LiteralLikeNode, PrimaryExpression {
|
||||
_literalExpressionBrand: any;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.TemplateHead)
|
||||
// @kind(SyntaxKind.TemplateMiddle)
|
||||
// @kind(SyntaxKind.TemplateTail)
|
||||
export interface LiteralExpression extends PrimaryExpression {
|
||||
text: string;
|
||||
isUnterminated?: boolean;
|
||||
hasExtendedUnicodeEscape?: boolean;
|
||||
export interface TemplateLiteralFragment extends LiteralLikeNode {
|
||||
_templateLiteralFragmentBrand: any;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.TemplateExpression)
|
||||
export interface TemplateExpression extends PrimaryExpression {
|
||||
head: LiteralExpression;
|
||||
head: TemplateLiteralFragment;
|
||||
templateSpans: NodeArray<TemplateSpan>;
|
||||
}
|
||||
|
||||
|
@ -937,7 +949,7 @@ namespace ts {
|
|||
// @kind(SyntaxKind.TemplateSpan)
|
||||
export interface TemplateSpan extends Node {
|
||||
expression: Expression;
|
||||
literal: LiteralExpression;
|
||||
literal: TemplateLiteralFragment;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.ParenthesizedExpression)
|
||||
|
|
|
@ -466,9 +466,6 @@ namespace ts {
|
|||
return true;
|
||||
case SyntaxKind.VoidKeyword:
|
||||
return node.parent.kind !== SyntaxKind.VoidExpression;
|
||||
case SyntaxKind.StringLiteral:
|
||||
// Specialized signatures can have string literals as their parameters' type names
|
||||
return node.parent.kind === SyntaxKind.Parameter;
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
return !isExpressionWithTypeArgumentsInClassExtendsClause(node);
|
||||
|
||||
|
|
|
@ -3372,6 +3372,7 @@ namespace ts {
|
|||
|
||||
function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean {
|
||||
if (contextToken.kind === SyntaxKind.StringLiteral
|
||||
|| contextToken.kind === SyntaxKind.StringLiteralType
|
||||
|| contextToken.kind === SyntaxKind.RegularExpressionLiteral
|
||||
|| isTemplateLiteralKind(contextToken.kind)) {
|
||||
let start = contextToken.getStart();
|
||||
|
@ -6369,6 +6370,7 @@ namespace ts {
|
|||
case SyntaxKind.PropertyAccessExpression:
|
||||
case SyntaxKind.QualifiedName:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.StringLiteralType:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
|
@ -6830,7 +6832,7 @@ namespace ts {
|
|||
else if (tokenKind === SyntaxKind.NumericLiteral) {
|
||||
return ClassificationType.numericLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.StringLiteral) {
|
||||
else if (tokenKind === SyntaxKind.StringLiteral || tokenKind === SyntaxKind.StringLiteralType) {
|
||||
return ClassificationType.stringLiteral;
|
||||
}
|
||||
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
|
||||
|
@ -7749,7 +7751,7 @@ namespace ts {
|
|||
addResult(start, end, classFromKind(token));
|
||||
|
||||
if (end >= text.length) {
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
if (token === SyntaxKind.StringLiteral || token === SyntaxKind.StringLiteralType) {
|
||||
// Check to see if we finished up on a multiline string literal.
|
||||
let tokenText = scanner.getTokenText();
|
||||
if (scanner.isUnterminated()) {
|
||||
|
@ -7899,6 +7901,7 @@ namespace ts {
|
|||
case SyntaxKind.NumericLiteral:
|
||||
return ClassificationType.numericLiteral;
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.StringLiteralType:
|
||||
return ClassificationType.stringLiteral;
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
return ClassificationType.regularExpressionLiteral;
|
||||
|
|
|
@ -257,14 +257,14 @@ namespace ts {
|
|||
return syntaxList;
|
||||
}
|
||||
|
||||
/* Gets the token whose text has range [start, end) and
|
||||
/* Gets the token whose text has range [start, end) and
|
||||
* position >= start and (position < end or (position === end && token is keyword or identifier))
|
||||
*/
|
||||
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
|
||||
return getTouchingToken(sourceFile, position, n => isWord(n.kind));
|
||||
}
|
||||
|
||||
/* Gets the token whose text has range [start, end) and position >= start
|
||||
/* Gets the token whose text has range [start, end) and position >= start
|
||||
* and (position < end or (position === end && token is keyword or identifier or numeric\string litera))
|
||||
*/
|
||||
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
|
||||
|
@ -391,8 +391,8 @@ namespace ts {
|
|||
const start = child.getStart(sourceFile);
|
||||
const lookInPreviousChild =
|
||||
(start >= position) || // cursor in the leading trivia
|
||||
(child.kind === SyntaxKind.JsxText && start === child.end); // whitespace only JsxText
|
||||
|
||||
(child.kind === SyntaxKind.JsxText && start === child.end); // whitespace only JsxText
|
||||
|
||||
if (lookInPreviousChild) {
|
||||
// actual start of the node is past the position - previous token should be at the end of previous child
|
||||
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
|
||||
|
@ -407,7 +407,7 @@ namespace ts {
|
|||
|
||||
Debug.assert(startNode !== undefined || n.kind === SyntaxKind.SourceFile);
|
||||
|
||||
// Here we know that none of child token nodes embrace the position,
|
||||
// Here we know that none of child token nodes embrace the position,
|
||||
// the only known case is when position is at the end of the file.
|
||||
// Try to find the rightmost token in the file without filtering.
|
||||
// Namely we are skipping the check: 'position < node.end'
|
||||
|
@ -429,7 +429,7 @@ namespace ts {
|
|||
|
||||
export function isInString(sourceFile: SourceFile, position: number) {
|
||||
let token = getTokenAtPosition(sourceFile, position);
|
||||
return token && token.kind === SyntaxKind.StringLiteral && position > token.getStart();
|
||||
return token && (token.kind === SyntaxKind.StringLiteral || token.kind === SyntaxKind.StringLiteralType) && position > token.getStart();
|
||||
}
|
||||
|
||||
export function isInComment(sourceFile: SourceFile, position: number) {
|
||||
|
@ -445,7 +445,7 @@ namespace ts {
|
|||
|
||||
if (token && position <= token.getStart()) {
|
||||
let commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
|
||||
|
||||
// The end marker of a single-line comment does not include the newline character.
|
||||
// In the following case, we are inside a comment (^ denotes the cursor position):
|
||||
//
|
||||
|
@ -565,6 +565,7 @@ namespace ts {
|
|||
|
||||
export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind): boolean {
|
||||
if (kind === SyntaxKind.StringLiteral
|
||||
|| kind === SyntaxKind.StringLiteralType
|
||||
|| kind === SyntaxKind.RegularExpressionLiteral
|
||||
|| isTemplateLiteralKind(kind)) {
|
||||
return true;
|
||||
|
|
Loading…
Reference in a new issue