Skip to content

Commit 8909c3a

Browse files
committed
Merge pull request #5725 from Microsoft/stringTypesCleanup
Disambiguate string literal types from other string literals.
2 parents d0de238 + 9b0231d commit 8909c3a

File tree

8 files changed

+72
-48
lines changed

8 files changed

+72
-48
lines changed

src/compiler/checker.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ namespace ts {
532532
}
533533

534534
// Because of module/namespace merging, a module's exports are in scope,
535-
// yet we never want to treat an export specifier as putting a member in scope.
535+
// yet we never want to treat an export specifier as putting a member in scope.
536536
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
537537
// Two things to note about this:
538538
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
@@ -3197,7 +3197,7 @@ namespace ts {
31973197
case SyntaxKind.BooleanKeyword:
31983198
case SyntaxKind.SymbolKeyword:
31993199
case SyntaxKind.VoidKeyword:
3200-
case SyntaxKind.StringLiteral:
3200+
case SyntaxKind.StringLiteralType:
32013201
return true;
32023202
case SyntaxKind.ArrayType:
32033203
return isIndependentType((<ArrayTypeNode>node).elementType);
@@ -3858,7 +3858,7 @@ namespace ts {
38583858
paramSymbol = resolvedSymbol;
38593859
}
38603860
parameters.push(paramSymbol);
3861-
if (param.type && param.type.kind === SyntaxKind.StringLiteral) {
3861+
if (param.type && param.type.kind === SyntaxKind.StringLiteralType) {
38623862
hasStringLiterals = true;
38633863
}
38643864

@@ -4527,8 +4527,7 @@ namespace ts {
45274527
return links.resolvedType;
45284528
}
45294529

4530-
function getStringLiteralType(node: StringLiteral): StringLiteralType {
4531-
const text = node.text;
4530+
function getStringLiteralType(text: string): StringLiteralType {
45324531
if (hasProperty(stringLiteralTypes, text)) {
45334532
return stringLiteralTypes[text];
45344533
}
@@ -4538,10 +4537,10 @@ namespace ts {
45384537
return type;
45394538
}
45404539

4541-
function getTypeFromStringLiteral(node: StringLiteral): Type {
4540+
function getTypeFromStringLiteral(node: StringLiteral | StringLiteralTypeNode): Type {
45424541
const links = getNodeLinks(node);
45434542
if (!links.resolvedType) {
4544-
links.resolvedType = getStringLiteralType(node);
4543+
links.resolvedType = getStringLiteralType(node.text);
45454544
}
45464545
return links.resolvedType;
45474546
}
@@ -4583,8 +4582,8 @@ namespace ts {
45834582
return voidType;
45844583
case SyntaxKind.ThisType:
45854584
return getTypeFromThisTypeNode(node);
4586-
case SyntaxKind.StringLiteral:
4587-
return getTypeFromStringLiteral(<StringLiteral>node);
4585+
case SyntaxKind.StringLiteralType:
4586+
return getTypeFromStringLiteral(<StringLiteralTypeNode>node);
45884587
case SyntaxKind.TypeReference:
45894588
return getTypeFromTypeReference(<TypeReferenceNode>node);
45904589
case SyntaxKind.TypePredicate:
@@ -8791,7 +8790,7 @@ namespace ts {
87918790
// for the argument. In that case, we should check the argument.
87928791
if (argType === undefined) {
87938792
argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors
8794-
? getStringLiteralType(<StringLiteral>arg)
8793+
? getStringLiteralType((<StringLiteral>arg).text)
87958794
: checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
87968795
}
87978796

@@ -8986,7 +8985,7 @@ namespace ts {
89868985
case SyntaxKind.Identifier:
89878986
case SyntaxKind.NumericLiteral:
89888987
case SyntaxKind.StringLiteral:
8989-
return getStringLiteralType(<StringLiteral>element.name);
8988+
return getStringLiteralType((<Identifier | LiteralExpression>element.name).text);
89908989

89918990
case SyntaxKind.ComputedPropertyName:
89928991
const nameType = checkComputedPropertyName(<ComputedPropertyName>element.name);
@@ -10608,7 +10607,8 @@ namespace ts {
1060810607
function checkStringLiteralExpression(node: StringLiteral): Type {
1060910608
const contextualType = getContextualType(node);
1061010609
if (contextualType && contextualTypeIsStringLiteralType(contextualType)) {
10611-
return getStringLiteralType(node);
10610+
// TODO (drosen): Consider using getTypeFromStringLiteral instead
10611+
return getStringLiteralType(node.text);
1061210612
}
1061310613

1061410614
return stringType;
@@ -11442,7 +11442,7 @@ namespace ts {
1144211442
// we can get here in two cases
1144311443
// 1. mixed static and instance class members
1144411444
// 2. something with the same name was defined before the set of overloads that prevents them from merging
11445-
// here we'll report error only for the first case since for second we should already report error in binder
11445+
// here we'll report error only for the first case since for second we should already report error in binder
1144611446
if (reportError) {
1144711447
const diagnostic = node.flags & NodeFlags.Static ? Diagnostics.Function_overload_must_be_static : Diagnostics.Function_overload_must_not_be_static;
1144811448
error(errorNode, diagnostic);

src/compiler/declarationEmitter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ namespace ts {
357357
case SyntaxKind.SymbolKeyword:
358358
case SyntaxKind.VoidKeyword:
359359
case SyntaxKind.ThisType:
360-
case SyntaxKind.StringLiteral:
360+
case SyntaxKind.StringLiteralType:
361361
return writeTextOfNode(currentText, type);
362362
case SyntaxKind.ExpressionWithTypeArguments:
363363
return emitExpressionWithTypeArguments(<ExpressionWithTypeArguments>type);

src/compiler/emitter.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -1276,7 +1276,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
12761276
}
12771277
}
12781278

1279-
function isBinaryOrOctalIntegerLiteral(node: LiteralExpression, text: string): boolean {
1279+
function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string): boolean {
12801280
if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) {
12811281
switch (text.charCodeAt(1)) {
12821282
case CharacterCodes.b:
@@ -1290,7 +1290,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
12901290
return false;
12911291
}
12921292

1293-
function emitLiteral(node: LiteralExpression) {
1293+
function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) {
12941294
const text = getLiteralText(node);
12951295

12961296
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
13051305
}
13061306
}
13071307

1308-
function getLiteralText(node: LiteralExpression) {
1308+
function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) {
13091309
// Any template literal or string literal with an extended escape
13101310
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
13111311
if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
@@ -1364,7 +1364,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
13641364
write(`"${text}"`);
13651365
}
13661366

1367-
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression) => void) {
1367+
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression | TemplateLiteralFragment) => void) {
13681368
write("[");
13691369
if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
13701370
literalEmitter(<LiteralExpression>node.template);
@@ -5954,7 +5954,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
59545954

59555955
function emitSerializedTypeNode(node: TypeNode) {
59565956
if (node) {
5957-
59585957
switch (node.kind) {
59595958
case SyntaxKind.VoidKeyword:
59605959
write("void 0");
@@ -5980,7 +5979,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
59805979
return;
59815980

59825981
case SyntaxKind.StringKeyword:
5983-
case SyntaxKind.StringLiteral:
5982+
case SyntaxKind.StringLiteralType:
59845983
write("String");
59855984
return;
59865985

src/compiler/parser.ts

+18-6
Original file line numberDiff line numberDiff line change
@@ -1874,7 +1874,7 @@ namespace ts {
18741874
function parseTemplateExpression(): TemplateExpression {
18751875
const template = <TemplateExpression>createNode(SyntaxKind.TemplateExpression);
18761876

1877-
template.head = parseLiteralNode();
1877+
template.head = parseTemplateLiteralFragment();
18781878
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
18791879

18801880
const templateSpans = <NodeArray<TemplateSpan>>[];
@@ -1895,22 +1895,34 @@ namespace ts {
18951895
const span = <TemplateSpan>createNode(SyntaxKind.TemplateSpan);
18961896
span.expression = allowInAnd(parseExpression);
18971897

1898-
let literal: LiteralExpression;
1898+
let literal: TemplateLiteralFragment;
18991899

19001900
if (token === SyntaxKind.CloseBraceToken) {
19011901
reScanTemplateToken();
1902-
literal = parseLiteralNode();
1902+
literal = parseTemplateLiteralFragment();
19031903
}
19041904
else {
1905-
literal = <LiteralExpression>parseExpectedToken(SyntaxKind.TemplateTail, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken));
1905+
literal = <TemplateLiteralFragment>parseExpectedToken(SyntaxKind.TemplateTail, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken));
19061906
}
19071907

19081908
span.literal = literal;
19091909
return finishNode(span);
19101910
}
19111911

1912+
function parseStringLiteralTypeNode(): StringLiteralTypeNode {
1913+
return <StringLiteralTypeNode>parseLiteralLikeNode(SyntaxKind.StringLiteralType, /*internName*/ true);
1914+
}
1915+
19121916
function parseLiteralNode(internName?: boolean): LiteralExpression {
1913-
const node = <LiteralExpression>createNode(token);
1917+
return <LiteralExpression>parseLiteralLikeNode(token, internName);
1918+
}
1919+
1920+
function parseTemplateLiteralFragment(): TemplateLiteralFragment {
1921+
return <TemplateLiteralFragment>parseLiteralLikeNode(token, /*internName*/ false);
1922+
}
1923+
1924+
function parseLiteralLikeNode(kind: SyntaxKind, internName: boolean): LiteralLikeNode {
1925+
const node = <LiteralExpression>createNode(kind);
19141926
const text = scanner.getTokenValue();
19151927
node.text = internName ? internIdentifier(text) : text;
19161928

@@ -2397,7 +2409,7 @@ namespace ts {
23972409
const node = tryParse(parseKeywordAndNoDot);
23982410
return node || parseTypeReferenceOrTypePredicate();
23992411
case SyntaxKind.StringLiteral:
2400-
return <StringLiteral>parseLiteralNode(/*internName*/ true);
2412+
return parseStringLiteralTypeNode();
24012413
case SyntaxKind.VoidKeyword:
24022414
return parseTokenNode<TypeNode>();
24032415
case SyntaxKind.ThisKeyword:

src/compiler/types.ts

+22-10
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ namespace ts {
205205
IntersectionType,
206206
ParenthesizedType,
207207
ThisType,
208+
StringLiteralType,
208209
// Binding patterns
209210
ObjectBindingPattern,
210211
ArrayBindingPattern,
@@ -350,7 +351,7 @@ namespace ts {
350351
FirstFutureReservedWord = ImplementsKeyword,
351352
LastFutureReservedWord = YieldKeyword,
352353
FirstTypeNode = TypePredicate,
353-
LastTypeNode = ThisType,
354+
LastTypeNode = StringLiteralType,
354355
FirstPunctuation = OpenBraceToken,
355356
LastPunctuation = CaretEqualsToken,
356357
FirstToken = Unknown,
@@ -790,10 +791,13 @@ namespace ts {
790791
type: TypeNode;
791792
}
792793

793-
// Note that a StringLiteral AST node is both an Expression and a TypeNode. The latter is
794-
// because string literals can appear in type annotations as well.
794+
// @kind(SyntaxKind.StringLiteralType)
795+
export interface StringLiteralTypeNode extends LiteralLikeNode, TypeNode {
796+
_stringLiteralTypeBrand: any;
797+
}
798+
795799
// @kind(SyntaxKind.StringLiteral)
796-
export interface StringLiteral extends LiteralExpression, TypeNode {
800+
export interface StringLiteral extends LiteralExpression {
797801
_stringLiteralBrand: any;
798802
}
799803

@@ -911,24 +915,32 @@ namespace ts {
911915
body: ConciseBody;
912916
}
913917

918+
export interface LiteralLikeNode extends Node {
919+
text: string;
920+
isUnterminated?: boolean;
921+
hasExtendedUnicodeEscape?: boolean;
922+
}
923+
914924
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
915925
// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters.
916926
// 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".
917927
// @kind(SyntaxKind.NumericLiteral)
918928
// @kind(SyntaxKind.RegularExpressionLiteral)
919929
// @kind(SyntaxKind.NoSubstitutionTemplateLiteral)
930+
export interface LiteralExpression extends LiteralLikeNode, PrimaryExpression {
931+
_literalExpressionBrand: any;
932+
}
933+
920934
// @kind(SyntaxKind.TemplateHead)
921935
// @kind(SyntaxKind.TemplateMiddle)
922936
// @kind(SyntaxKind.TemplateTail)
923-
export interface LiteralExpression extends PrimaryExpression {
924-
text: string;
925-
isUnterminated?: boolean;
926-
hasExtendedUnicodeEscape?: boolean;
937+
export interface TemplateLiteralFragment extends LiteralLikeNode {
938+
_templateLiteralFragmentBrand: any;
927939
}
928940

929941
// @kind(SyntaxKind.TemplateExpression)
930942
export interface TemplateExpression extends PrimaryExpression {
931-
head: LiteralExpression;
943+
head: TemplateLiteralFragment;
932944
templateSpans: NodeArray<TemplateSpan>;
933945
}
934946

@@ -937,7 +949,7 @@ namespace ts {
937949
// @kind(SyntaxKind.TemplateSpan)
938950
export interface TemplateSpan extends Node {
939951
expression: Expression;
940-
literal: LiteralExpression;
952+
literal: TemplateLiteralFragment;
941953
}
942954

943955
// @kind(SyntaxKind.ParenthesizedExpression)

src/compiler/utilities.ts

-3
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,6 @@ namespace ts {
466466
return true;
467467
case SyntaxKind.VoidKeyword:
468468
return node.parent.kind !== SyntaxKind.VoidExpression;
469-
case SyntaxKind.StringLiteral:
470-
// Specialized signatures can have string literals as their parameters' type names
471-
return node.parent.kind === SyntaxKind.Parameter;
472469
case SyntaxKind.ExpressionWithTypeArguments:
473470
return !isExpressionWithTypeArgumentsInClassExtendsClause(node);
474471

src/services/services.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -3372,6 +3372,7 @@ namespace ts {
33723372

33733373
function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean {
33743374
if (contextToken.kind === SyntaxKind.StringLiteral
3375+
|| contextToken.kind === SyntaxKind.StringLiteralType
33753376
|| contextToken.kind === SyntaxKind.RegularExpressionLiteral
33763377
|| isTemplateLiteralKind(contextToken.kind)) {
33773378
let start = contextToken.getStart();
@@ -6369,6 +6370,7 @@ namespace ts {
63696370
case SyntaxKind.PropertyAccessExpression:
63706371
case SyntaxKind.QualifiedName:
63716372
case SyntaxKind.StringLiteral:
6373+
case SyntaxKind.StringLiteralType:
63726374
case SyntaxKind.FalseKeyword:
63736375
case SyntaxKind.TrueKeyword:
63746376
case SyntaxKind.NullKeyword:
@@ -6830,7 +6832,7 @@ namespace ts {
68306832
else if (tokenKind === SyntaxKind.NumericLiteral) {
68316833
return ClassificationType.numericLiteral;
68326834
}
6833-
else if (tokenKind === SyntaxKind.StringLiteral) {
6835+
else if (tokenKind === SyntaxKind.StringLiteral || tokenKind === SyntaxKind.StringLiteralType) {
68346836
return ClassificationType.stringLiteral;
68356837
}
68366838
else if (tokenKind === SyntaxKind.RegularExpressionLiteral) {
@@ -7749,7 +7751,7 @@ namespace ts {
77497751
addResult(start, end, classFromKind(token));
77507752

77517753
if (end >= text.length) {
7752-
if (token === SyntaxKind.StringLiteral) {
7754+
if (token === SyntaxKind.StringLiteral || token === SyntaxKind.StringLiteralType) {
77537755
// Check to see if we finished up on a multiline string literal.
77547756
let tokenText = scanner.getTokenText();
77557757
if (scanner.isUnterminated()) {
@@ -7899,6 +7901,7 @@ namespace ts {
78997901
case SyntaxKind.NumericLiteral:
79007902
return ClassificationType.numericLiteral;
79017903
case SyntaxKind.StringLiteral:
7904+
case SyntaxKind.StringLiteralType:
79027905
return ClassificationType.stringLiteral;
79037906
case SyntaxKind.RegularExpressionLiteral:
79047907
return ClassificationType.regularExpressionLiteral;

0 commit comments

Comments
 (0)