Template literal types and mapped type 'as' clauses (#40336)

* Initial implementation of string template types

* Accept new API baselines

* Accept new baselines

* Unified checking for large cross product union types

* Accept new baselines

* Ensure errors from union type resolution are reported

* Accept new baselines

* Compute constraints for string template types

* Support `as T` clause in mapped types

* Accept new API baselines

* Add missing semicolon

* Add checking of `as T` clauses

* Support casing modifiers in string template types

* Accept new baselines

* Bump keyword maximum length

* fix anders

* Revert "fix anders"

This reverts commit b3178d4618.

* Properly handle 'as T' clause with keyof for mapped type

* Fix lint error

* Single character inferences and anchored end span matching

* Fewer array copy operations in template literal type resolution

* Handle cases where 'as T' maps multiple properties onto one

* Fix lint error

* Store key type instead of type mapper in MappedSymbol

* No constraint on `in T` type when `as N` clause present

* Rename from TemplateType to TemplateLiteralType

* Accept new API baselines

* Add tests

* Accept new baselines

* Address CR feedback

* Accept new API baselines

Co-authored-by: Erich Gamma <egamma@microsoft.com>
This commit is contained in:
Anders Hejlsberg
2020-09-09 17:23:22 -10:00
committed by GitHub
parent 96b0832cf6
commit 6f0c91c4cb
40 changed files with 3474 additions and 583 deletions

View File

@@ -206,6 +206,7 @@ namespace ts {
case SyntaxKind.MappedType:
return visitNode(cbNode, (<MappedTypeNode>node).readonlyToken) ||
visitNode(cbNode, (<MappedTypeNode>node).typeParameter) ||
visitNode(cbNode, (<MappedTypeNode>node).nameType) ||
visitNode(cbNode, (<MappedTypeNode>node).questionToken) ||
visitNode(cbNode, (<MappedTypeNode>node).type);
case SyntaxKind.LiteralType:
@@ -424,6 +425,10 @@ namespace ts {
return visitNode(cbNode, (<TemplateExpression>node).head) || visitNodes(cbNode, cbNodes, (<TemplateExpression>node).templateSpans);
case SyntaxKind.TemplateSpan:
return visitNode(cbNode, (<TemplateSpan>node).expression) || visitNode(cbNode, (<TemplateSpan>node).literal);
case SyntaxKind.TemplateLiteralType:
return visitNode(cbNode, (<TemplateLiteralTypeNode>node).head) || visitNodes(cbNode, cbNodes, (<TemplateLiteralTypeNode>node).templateSpans);
case SyntaxKind.TemplateLiteralTypeSpan:
return visitNode(cbNode, (<TemplateLiteralTypeSpan>node).type) || visitNode(cbNode, (<TemplateLiteralTypeSpan>node).literal);
case SyntaxKind.ComputedPropertyName:
return visitNode(cbNode, (<ComputedPropertyName>node).expression);
case SyntaxKind.HeritageClause:
@@ -2584,6 +2589,49 @@ namespace ts {
);
}
function parseTemplateType(): TemplateLiteralTypeNode {
const pos = getNodePos();
return finishNode(
factory.createTemplateLiteralType(
parseTemplateHead(/*isTaggedTemplate*/ false),
parseTemplateTypeSpans()
),
pos
);
}
function parseTemplateTypeSpans() {
const pos = getNodePos();
const list = [];
let node: TemplateLiteralTypeSpan;
do {
node = parseTemplateTypeSpan();
list.push(node);
}
while (node.literal.kind === SyntaxKind.TemplateMiddle);
return createNodeArray(list, pos);
}
function parseTemplateTypeSpan(): TemplateLiteralTypeSpan {
const pos = getNodePos();
return finishNode(
factory.createTemplateLiteralTypeSpan(
parseTemplateCasing(),
parseType(),
parseLiteralOfTemplateSpan(/*isTaggedTemplate*/ false)
),
pos
);
}
function parseTemplateCasing(): TemplateCasing {
return parseOptional(SyntaxKind.UppercaseKeyword) ? TemplateCasing.Uppercase :
parseOptional(SyntaxKind.LowercaseKeyword) ? TemplateCasing.Lowercase :
parseOptional(SyntaxKind.CapitalizeKeyword) ? TemplateCasing.Capitalize :
parseOptional(SyntaxKind.UncapitalizeKeyword) ? TemplateCasing.Uncapitalize :
TemplateCasing.None;
}
function parseLiteralOfTemplateSpan(isTaggedTemplate: boolean) {
if (token() === SyntaxKind.CloseBraceToken) {
reScanTemplateToken(isTaggedTemplate);
@@ -3252,6 +3300,7 @@ namespace ts {
}
parseExpected(SyntaxKind.OpenBracketToken);
const typeParameter = parseMappedTypeParameter();
const nameType = parseOptional(SyntaxKind.AsKeyword) ? parseType() : undefined;
parseExpected(SyntaxKind.CloseBracketToken);
let questionToken: QuestionToken | PlusToken | MinusToken | undefined;
if (token() === SyntaxKind.QuestionToken || token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) {
@@ -3263,7 +3312,7 @@ namespace ts {
const type = parseTypeAnnotation();
parseSemicolon();
parseExpected(SyntaxKind.CloseBraceToken);
return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, questionToken, type), pos);
return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type), pos);
}
function parseTupleElementType() {
@@ -3444,6 +3493,8 @@ namespace ts {
return parseImportType();
case SyntaxKind.AssertsKeyword:
return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine) ? parseAssertsTypePredicate() : parseTypeReference();
case SyntaxKind.TemplateHead:
return parseTemplateType();
default:
return parseTypeReference();
}
@@ -3485,6 +3536,8 @@ namespace ts {
case SyntaxKind.InferKeyword:
case SyntaxKind.ImportKeyword:
case SyntaxKind.AssertsKeyword:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.TemplateHead:
return true;
case SyntaxKind.FunctionKeyword:
return !inStartOfParameter;