WIP promises type

This commit is contained in:
Ron Buckton 2017-01-06 16:47:26 -08:00
parent 46622740b2
commit 4af87e1efc
8 changed files with 173 additions and 43 deletions

View File

@ -2710,6 +2710,7 @@ namespace ts {
break;
case SyntaxKind.ImplementsKeyword:
case SyntaxKind.PromisesKeyword:
// An `implements` HeritageClause is TypeScript syntax.
transformFlags |= TransformFlags.AssertTypeScript;
break;

View File

@ -7740,15 +7740,18 @@ namespace ts {
result = mappedTypeRelatedTo(source, target, reportErrors);
}
else {
result = propertiesRelatedTo(source, target, reportErrors);
result = promisesRelatedTo(source, target, reportErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors);
result = propertiesRelatedTo(source, target, reportErrors);
if (result) {
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors);
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportErrors);
if (result) {
result &= indexTypesRelatedTo(source, originalSource, target, IndexKind.String, reportErrors);
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportErrors);
if (result) {
result &= indexTypesRelatedTo(source, originalSource, target, IndexKind.Number, reportErrors);
result &= indexTypesRelatedTo(source, originalSource, target, IndexKind.String, reportErrors);
if (result) {
result &= indexTypesRelatedTo(source, originalSource, target, IndexKind.Number, reportErrors);
}
}
}
}
@ -7802,6 +7805,10 @@ namespace ts {
return Ternary.False;
}
function promisesRelatedTo(_source: Type, _target: Type, _reportErrors: boolean): Ternary {
return Ternary.Maybe;
}
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
if (relation === identityRelation) {
return propertiesIdenticalTo(source, target);
@ -18234,6 +18241,28 @@ namespace ts {
}
}
function checkPromisesTypesIdentical(node: ClassLikeDeclaration | InterfaceDeclaration, symbol: Symbol) {
if (symbol.declarations.length === 1) {
return;
}
let firstPromisesType: Type;
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
if (!firstPromisesType) {
const firstPromisesTypeNode = getPromisesHeritageClauseElement(<ClassLikeDeclaration | InterfaceDeclaration>declaration);
firstPromisesType = firstPromisesTypeNode && getTypeFromTypeNode(firstPromisesTypeNode);
}
else {
const promisesTypeNode = getPromisesHeritageClauseElement(<ClassLikeDeclaration | InterfaceDeclaration>declaration);
const promisesType = promisesTypeNode && getTypeFromTypeNode(promisesTypeNode);
if (promisesTypeNode && !isTypeIdenticalTo(firstPromisesType, promisesType)) {
error(node.name, Diagnostics.Any_declarations_of_0_that_promise_a_type_must_promise_the_same_type, node.name.text);
}
}
}
}
}
function checkClassExpression(node: ClassExpression): Type {
checkClassLikeDeclaration(node);
checkNodeDeferred(node);
@ -18272,6 +18301,7 @@ namespace ts {
const typeWithThis = getTypeWithThisArgument(type);
const staticType = <ObjectType>getTypeOfSymbol(symbol);
checkTypeParameterListsIdentical(node, symbol);
checkPromisesTypesIdentical(node, symbol);
checkClassForDuplicateDeclarations(node);
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
@ -18342,6 +18372,26 @@ namespace ts {
}
}
const promisesTypeNode = getPromisesHeritageClauseElement(node);
if (promisesTypeNode) {
if (!isEntityNameExpression(promisesTypeNode.expression)) {
error(promisesTypeNode.expression, Diagnostics.A_class_or_interface_can_only_promise_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkTypeReferenceNode(promisesTypeNode);
if (produceDiagnostics) {
const t = getTypeFromTypeNode(promisesTypeNode);
if (t !== unknownType) {
const promisedType = getPromisedTypeOfPromise(type);
if (!promisedType) {
error(node.name || node, Diagnostics.Class_or_interface_0_incorrectly_promises_type_1_A_compatible_then_signature_could_not_be_found, typeToString(type), typeToString(t));
}
else {
checkTypeAssignableTo(t, promisedType, node.name || node, Diagnostics.Class_or_interface_promises_type_0_which_is_not_compatible_with_the_type_1_used_by_the_fulfillment_callbacks_of_its_then_members);
}
}
}
}
if (produceDiagnostics) {
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
@ -18541,6 +18591,14 @@ namespace ts {
// Grammar checking
checkGrammarDecorators(node) || checkGrammarModifiers(node) || checkGrammarInterfaceDeclaration(node);
const promisesTypeNode = getPromisesHeritageClauseElement(node);
if (promisesTypeNode) {
if (!isEntityNameExpression(promisesTypeNode.expression)) {
error(promisesTypeNode.expression, Diagnostics.A_class_or_interface_can_only_promise_an_identifier_Slashqualified_name_with_optional_type_arguments);
}
checkTypeReferenceNode(promisesTypeNode);
}
checkTypeParameters(node.typeParameters);
if (produceDiagnostics) {
checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0);
@ -18548,6 +18606,7 @@ namespace ts {
checkExportsOnMergedDeclarations(node);
const symbol = getSymbolOfNode(node);
checkTypeParameterListsIdentical(node, symbol);
checkPromisesTypesIdentical(node, symbol);
// Only check this symbol once
const firstInterfaceDecl = <InterfaceDeclaration>getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
@ -18561,6 +18620,19 @@ namespace ts {
}
checkIndexConstraints(type);
}
if (promisesTypeNode) {
const promisesType = getTypeFromTypeNode(promisesTypeNode);
if (promisesType !== unknownType) {
const promisedType = getPromisedTypeOfPromise(type);
if (!promisedType) {
error(node.name || node, Diagnostics.Class_or_interface_0_incorrectly_promises_type_1_A_compatible_then_signature_could_not_be_found, typeToString(type), typeToString(promisesType));
}
else {
checkTypeAssignableTo(promisesType, promisedType, node.name || node, Diagnostics.Class_or_interface_promises_type_0_which_is_not_compatible_with_the_type_1_used_by_the_fulfillment_callbacks_of_its_then_members);
}
}
}
}
checkObjectTypeForDuplicateDeclarations(node);
}
@ -21343,31 +21415,46 @@ namespace ts {
function checkGrammarClassDeclarationHeritageClauses(node: ClassLikeDeclaration) {
let seenExtendsClause = false;
let seenImplementsClause = false;
let seenPromisesClause = false;
if (!checkGrammarDecorators(node) && !checkGrammarModifiers(node) && node.heritageClauses) {
for (const heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen);
}
switch (heritageClause.token) {
case SyntaxKind.ExtendsKeyword:
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_already_seen, "extends");
}
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_must_precede_0_clause, "extends", "implements");
}
if (seenPromisesClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_must_precede_0_clause, "extends", "promises");
}
if (heritageClause.types.length > 1) {
return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_can_only_extend_a_single_class);
}
seenExtendsClause = true;
break;
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_must_precede_implements_clause);
}
case SyntaxKind.ImplementsKeyword:
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_already_seen, "implements");
}
if (seenPromisesClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_must_precede_0_clause, "implements", "promises");
}
seenImplementsClause = true;
break;
if (heritageClause.types.length > 1) {
return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_can_only_extend_a_single_class);
}
seenExtendsClause = true;
}
else {
Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword);
if (seenImplementsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.implements_clause_already_seen);
}
seenImplementsClause = true;
case SyntaxKind.PromisesKeyword:
if (seenPromisesClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_already_seen, "promises");
}
if (heritageClause.types.length > 1) {
return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_and_interfaces_can_only_promise_a_single_type);
}
seenPromisesClause = true;
break;
}
// Grammar checking heritageClause inside class declaration
@ -21378,19 +21465,31 @@ namespace ts {
function checkGrammarInterfaceDeclaration(node: InterfaceDeclaration) {
let seenExtendsClause = false;
let seenPromisesClause = false;
if (node.heritageClauses) {
for (const heritageClause of node.heritageClauses) {
if (heritageClause.token === SyntaxKind.ExtendsKeyword) {
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics.extends_clause_already_seen);
}
seenExtendsClause = true;
}
else {
Debug.assert(heritageClause.token === SyntaxKind.ImplementsKeyword);
return grammarErrorOnFirstToken(heritageClause, Diagnostics.Interface_declaration_cannot_have_implements_clause);
switch (heritageClause.token) {
case SyntaxKind.ExtendsKeyword:
if (seenExtendsClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_already_seen, "extends");
}
if (seenPromisesClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_must_precede_0_clause, "extends", "promises");
}
seenExtendsClause = true;
break;
case SyntaxKind.ImplementsKeyword:
return grammarErrorOnFirstToken(heritageClause, Diagnostics.Interface_declaration_cannot_have_implements_clause);
case SyntaxKind.PromisesKeyword:
if (seenPromisesClause) {
return grammarErrorOnFirstToken(heritageClause, Diagnostics._0_clause_already_seen, "promises");
}
if (heritageClause.types.length > 1) {
return grammarErrorOnFirstToken(heritageClause.types[1], Diagnostics.Classes_and_interfaces_can_only_promise_a_single_type);
}
seenPromisesClause = true;
break;
}
// Grammar checking heritageClause inside class declaration

View File

@ -511,11 +511,11 @@
"category": "Error",
"code": 1171
},
"'extends' clause already seen.": {
"'{0}' clause already seen.": {
"category": "Error",
"code": 1172
},
"'extends' clause must precede 'implements' clause.": {
"'{0}' clause must precede '{0}' clause.": {
"category": "Error",
"code": 1173
},
@ -523,7 +523,7 @@
"category": "Error",
"code": 1174
},
"'implements' clause already seen.": {
"Classes and interfaces can only promise a single type.": {
"category": "Error",
"code": 1175
},
@ -1323,6 +1323,10 @@
"category": "Error",
"code": 2420
},
"Class or interface '{0}' incorrectly promises type '{1}'. A compatible 'then()' signature could not be found.": {
"category": "Error",
"code": 2421
},
"A class may only implement another class or interface.": {
"category": "Error",
"code": 2422
@ -1351,6 +1355,10 @@
"category": "Error",
"code": 2428
},
"Class or interface promises type '{0}' which is not compatible with the type '{1}' used by the fulfillment callbacks of its 'then()' members.": {
"category": "Error",
"code": 2429
},
"Interface '{0}' incorrectly extends interface '{1}'.": {
"category": "Error",
"code": 2430
@ -1427,6 +1435,10 @@
"category": "Error",
"code": 2448
},
"Any declarations of '{0}' that promise a type must promise the same type.": {
"category": "Error",
"code": 2449
},
"Cannot redeclare block-scoped variable '{0}'.": {
"category": "Error",
"code": 2451
@ -1627,6 +1639,10 @@
"category": "Error",
"code": 2503
},
"A class or interface can only promise an identifier/qualified-name with optional type arguments.": {
"category": "Error",
"code": 2504
},
"A generator cannot have a 'void' type annotation.": {
"category": "Error",
"code": 2505

View File

@ -1333,7 +1333,7 @@ namespace ts {
// Clauses
export function createHeritageClause(token: SyntaxKind, types: ExpressionWithTypeArguments[], location?: TextRange) {
export function createHeritageClause(token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword | SyntaxKind.PromisesKeyword, types: ExpressionWithTypeArguments[], location?: TextRange) {
const node = <HeritageClause>createNode(SyntaxKind.HeritageClause, location);
node.token = token;
node.types = createNodeArray(types);

View File

@ -5412,9 +5412,10 @@ namespace ts {
}
function parseHeritageClause() {
if (token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword) {
const keyword = token();
if (keyword === SyntaxKind.ExtendsKeyword || keyword === SyntaxKind.ImplementsKeyword || keyword === SyntaxKind.PromisesKeyword) {
const node = <HeritageClause>createNode(SyntaxKind.HeritageClause);
node.token = token();
node.token = keyword;
nextToken();
node.types = parseDelimitedList(ParsingContext.HeritageClauseElement, parseExpressionWithTypeArguments);
return finishNode(node);
@ -5434,7 +5435,13 @@ namespace ts {
}
function isHeritageClause(): boolean {
return token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword;
switch (token()) {
case SyntaxKind.ExtendsKeyword:
case SyntaxKind.ImplementsKeyword:
case SyntaxKind.PromisesKeyword:
return true;
}
return false;
}
function parseClassMembers() {

View File

@ -101,6 +101,7 @@ namespace ts {
"package": SyntaxKind.PackageKeyword,
"private": SyntaxKind.PrivateKeyword,
"protected": SyntaxKind.ProtectedKeyword,
"promises": SyntaxKind.PromisesKeyword,
"public": SyntaxKind.PublicKeyword,
"readonly": SyntaxKind.ReadonlyKeyword,
"require": SyntaxKind.RequireKeyword,

View File

@ -172,6 +172,7 @@ namespace ts {
ModuleKeyword,
NamespaceKeyword,
NeverKeyword,
PromisesKeyword,
ReadonlyKeyword,
RequireKeyword,
NumberKeyword,
@ -1726,7 +1727,7 @@ namespace ts {
export interface HeritageClause extends Node {
kind: SyntaxKind.HeritageClause;
token: SyntaxKind;
token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword | SyntaxKind.PromisesKeyword;
types?: NodeArray<ExpressionWithTypeArguments>;
}

View File

@ -1806,6 +1806,11 @@ namespace ts {
return heritageClause ? heritageClause.types : undefined;
}
export function getPromisesHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) {
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.PromisesKeyword);
return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined;
}
export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration) {
const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
return heritageClause ? heritageClause.types : undefined;