mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-16 15:44:16 -06:00
Parsing and rudimentary checking of tuples with rest elements
This commit is contained in:
parent
09f17bcf57
commit
f1efd1d043
@ -3602,6 +3602,7 @@ namespace ts {
|
||||
case SyntaxKind.ArrayType:
|
||||
case SyntaxKind.TupleType:
|
||||
case SyntaxKind.OptionalType:
|
||||
case SyntaxKind.RestType:
|
||||
case SyntaxKind.UnionType:
|
||||
case SyntaxKind.IntersectionType:
|
||||
case SyntaxKind.ConditionalType:
|
||||
|
||||
@ -3400,9 +3400,12 @@ namespace ts {
|
||||
if (typeArguments.length > 0) {
|
||||
const arity = getTypeReferenceArity(type);
|
||||
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context);
|
||||
const hasRestElement = (<TupleType>type.target).hasRestElement;
|
||||
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
|
||||
for (let i = (<TupleType>type.target).minLength; i < arity; i++) {
|
||||
tupleConstituentNodes[i] = createOptionalTypeNode(tupleConstituentNodes[i]);
|
||||
tupleConstituentNodes[i] = hasRestElement && i === arity - 1 ?
|
||||
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
|
||||
createOptionalTypeNode(tupleConstituentNodes[i]);
|
||||
}
|
||||
return createTupleTypeNode(tupleConstituentNodes);
|
||||
}
|
||||
@ -4842,7 +4845,7 @@ namespace ts {
|
||||
}
|
||||
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
|
||||
const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
|
||||
let result = createTupleType(elementTypes);
|
||||
let result = <TypeReference>createTupleType(elementTypes);
|
||||
if (includePatternInType) {
|
||||
result = cloneTypeReference(result);
|
||||
result.pattern = pattern;
|
||||
@ -8282,21 +8285,25 @@ namespace ts {
|
||||
//
|
||||
// Note that the generic type created by this function has no symbol associated with it. The same
|
||||
// is true for each of the synthesized type parameters.
|
||||
function createTupleTypeOfArity(arity: number, minLength: number, associatedNames: __String[] | undefined): TupleType {
|
||||
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames: __String[] | undefined): TupleType {
|
||||
let typeParameters: TypeParameter[] | undefined;
|
||||
const properties: Symbol[] = [];
|
||||
const maxLength = hasRestElement ? arity - 1 : arity;
|
||||
if (arity) {
|
||||
typeParameters = new Array(arity);
|
||||
for (let i = 0; i < arity; i++) {
|
||||
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
|
||||
property.type = typeParameters[i] = <TypeParameter>createType(TypeFlags.TypeParameter);
|
||||
properties.push(property);
|
||||
const typeParameter = typeParameters[i] = <TypeParameter>createType(TypeFlags.TypeParameter);
|
||||
if (i < maxLength) {
|
||||
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
|
||||
property.type = typeParameter;
|
||||
properties.push(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
const literalTypes = [];
|
||||
for (let i = minLength; i <= arity; i++) literalTypes.push(getLiteralType(i));
|
||||
for (let i = minLength; i <= maxLength; i++) literalTypes.push(getLiteralType(i));
|
||||
const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String);
|
||||
lengthSymbol.type = getUnionType(literalTypes);
|
||||
lengthSymbol.type = hasRestElement ? numberType : getUnionType(literalTypes);
|
||||
properties.push(lengthSymbol);
|
||||
const type = <TupleType & InterfaceTypeWithDeclaredMembers>createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
|
||||
type.typeParameters = typeParameters;
|
||||
@ -8315,29 +8322,40 @@ namespace ts {
|
||||
type.declaredStringIndexInfo = undefined;
|
||||
type.declaredNumberIndexInfo = undefined;
|
||||
type.minLength = minLength;
|
||||
type.hasRestElement = hasRestElement;
|
||||
type.associatedNames = associatedNames;
|
||||
return type;
|
||||
}
|
||||
|
||||
function getTupleTypeOfArity(arity: number, minLength: number, associatedNames?: __String[]): GenericType {
|
||||
const key = arity + "," + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
|
||||
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames?: __String[]): GenericType {
|
||||
const key = arity + (hasRestElement ? "+" : ",") + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
|
||||
let type = tupleTypes.get(key);
|
||||
if (!type) {
|
||||
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, associatedNames));
|
||||
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, associatedNames));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function createTupleType(elementTypes: Type[], minLength = elementTypes.length, associatedNames?: __String[]) {
|
||||
const tupleType = getTupleTypeOfArity(elementTypes.length, minLength, associatedNames);
|
||||
function createTupleType(elementTypes: Type[], minLength = elementTypes.length, hasRestElement = false, associatedNames?: __String[]) {
|
||||
const arity = elementTypes.length;
|
||||
if (arity === 1 && hasRestElement) {
|
||||
return createArrayType(elementTypes[0]);
|
||||
}
|
||||
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, associatedNames);
|
||||
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
|
||||
}
|
||||
|
||||
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
const minLength = findLastIndex(node.elementTypes, n => n.kind !== SyntaxKind.OptionalType) + 1;
|
||||
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode), minLength);
|
||||
const lastElement = lastOrUndefined(node.elementTypes);
|
||||
const restElement = lastElement && lastElement.kind === SyntaxKind.RestType ? lastElement : undefined;
|
||||
const minLength = findLastIndex(node.elementTypes, n => n.kind !== SyntaxKind.OptionalType && n !== restElement) + 1;
|
||||
const elementTypes = map(node.elementTypes, n => {
|
||||
const type = getTypeFromTypeNode(n);
|
||||
return n === restElement ? getIndexTypeOfType(type, IndexKind.Number) || errorType : type;
|
||||
});
|
||||
links.resolvedType = createTupleType(elementTypes, minLength, !!restElement);
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
@ -9548,9 +9566,10 @@ namespace ts {
|
||||
case SyntaxKind.JSDocOptionalType:
|
||||
return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType).type));
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.RestType:
|
||||
case SyntaxKind.JSDocNonNullableType:
|
||||
case SyntaxKind.JSDocTypeExpression:
|
||||
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
|
||||
return getTypeFromTypeNode((<ParenthesizedTypeNode | RestTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
|
||||
case SyntaxKind.JSDocVariadicType:
|
||||
return getTypeFromJSDocVariadicType(node as JSDocVariadicType);
|
||||
case SyntaxKind.FunctionType:
|
||||
@ -11346,6 +11365,35 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isTupleType(target)) {
|
||||
const targetRestType = getRestTypeOfTupleType(<TypeReference>target);
|
||||
if (targetRestType) {
|
||||
if (!isTupleType(source)) {
|
||||
return Ternary.False;
|
||||
}
|
||||
const sourceRestType = getRestTypeOfTupleType(<TypeReference>source);
|
||||
if (sourceRestType && !isRelatedTo(sourceRestType, targetRestType, reportErrors)) {
|
||||
if (reportErrors) {
|
||||
// !!! Rest element types are incompatible
|
||||
reportError(Diagnostics.Index_signatures_are_incompatible);
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
const targetCount = getTypeReferenceArity(<TypeReference>target) - 1;
|
||||
const sourceCount = getTypeReferenceArity(<TypeReference>source) - (sourceRestType ? 1 : 0);
|
||||
for (let i = targetCount; i < sourceCount; i++) {
|
||||
const related = isRelatedTo((<TypeReference>source).typeArguments![i], targetRestType, reportErrors);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
// !!! Property {0} is incompatible with rest element type
|
||||
reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, "" + i);
|
||||
}
|
||||
return Ternary.False;
|
||||
}
|
||||
result &= related;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -12028,6 +12076,10 @@ namespace ts {
|
||||
return !!(getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target.objectFlags & ObjectFlags.Tuple);
|
||||
}
|
||||
|
||||
function getRestTypeOfTupleType(type: TypeReference) {
|
||||
return (<TupleType>type.target).hasRestElement ? type.typeArguments![type.target.typeParameters!.length - 1] : undefined;
|
||||
}
|
||||
|
||||
function getFalsyFlagsOfTypes(types: Type[]): TypeFlags {
|
||||
let result: TypeFlags = 0;
|
||||
for (const t of types) {
|
||||
@ -12400,7 +12452,7 @@ namespace ts {
|
||||
}
|
||||
const minArgumentCount = getMinArgumentCount(source);
|
||||
const minLength = minArgumentCount < paramCount ? 0 : minArgumentCount - paramCount;
|
||||
const rest = sourceHasRest ? createArrayType(getUnionType(types)) : createTupleType(types, minLength, names);
|
||||
const rest = sourceHasRest ? createArrayType(getUnionType(types)) : createTupleType(types, minLength, /*hasRestElement*/ false, names);
|
||||
callback(rest, targetRestTypeVariable);
|
||||
}
|
||||
}
|
||||
@ -15899,7 +15951,7 @@ namespace ts {
|
||||
// If array literal is actually a destructuring pattern, mark it as an implied type. We do this such
|
||||
// that we get the same behavior for "var [x, y] = []" and "[x, y] = []".
|
||||
if (inDestructuringPattern && elementTypes.length) {
|
||||
const type = cloneTypeReference(createTupleType(elementTypes));
|
||||
const type = cloneTypeReference(<TypeReference>createTupleType(elementTypes));
|
||||
type.pattern = node;
|
||||
return type;
|
||||
}
|
||||
@ -25639,6 +25691,7 @@ namespace ts {
|
||||
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.OptionalType:
|
||||
case SyntaxKind.RestType:
|
||||
return checkSourceElement((<ParenthesizedTypeNode | OptionalTypeNode>node).type);
|
||||
case SyntaxKind.TypeOperator:
|
||||
return checkTypeOperator(<TypeOperatorNode>node);
|
||||
|
||||
@ -678,8 +678,9 @@ namespace ts {
|
||||
return emitJSDocNonNullableType(node as JSDocNonNullableType);
|
||||
case SyntaxKind.JSDocOptionalType:
|
||||
return emitJSDocOptionalType(node as JSDocOptionalType);
|
||||
case SyntaxKind.RestType:
|
||||
case SyntaxKind.JSDocVariadicType:
|
||||
return emitJSDocVariadicType(node as JSDocVariadicType);
|
||||
return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType);
|
||||
|
||||
// Binding patterns
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
@ -1287,7 +1288,7 @@ namespace ts {
|
||||
writePunctuation("]");
|
||||
}
|
||||
|
||||
function emitJSDocVariadicType(node: JSDocVariadicType) {
|
||||
function emitRestOrJSDocVariadicType(node: RestTypeNode | JSDocVariadicType) {
|
||||
write("...");
|
||||
emit(node.type);
|
||||
}
|
||||
|
||||
@ -763,6 +763,18 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createRestTypeNode(type: TypeNode) {
|
||||
const node = createSynthesizedNode(SyntaxKind.RestType) as RestTypeNode;
|
||||
node.type = type;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateRestTypeNode(node: RestTypeNode, type: TypeNode): RestTypeNode {
|
||||
return node.type !== type
|
||||
? updateNode(createRestTypeNode(type), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createUnionTypeNode(types: ReadonlyArray<TypeNode>): UnionTypeNode {
|
||||
return <UnionTypeNode>createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types);
|
||||
}
|
||||
|
||||
@ -381,6 +381,7 @@ namespace ts {
|
||||
case SyntaxKind.ArrayType:
|
||||
case SyntaxKind.TupleType:
|
||||
case SyntaxKind.OptionalType:
|
||||
case SyntaxKind.RestType:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.TypePredicate:
|
||||
case SyntaxKind.TypeParameter:
|
||||
|
||||
@ -379,6 +379,10 @@ namespace ts {
|
||||
return updateOptionalTypeNode((<OptionalTypeNode>node),
|
||||
visitNode((<OptionalTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.RestType:
|
||||
return updateRestTypeNode((<RestTypeNode>node),
|
||||
visitNode((<RestTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.UnionType:
|
||||
return updateUnionTypeNode(<UnionTypeNode>node,
|
||||
nodesVisitor((<UnionTypeNode>node).types, visitor, isTypeNode));
|
||||
|
||||
@ -446,12 +446,13 @@ namespace ts {
|
||||
return visitNode(cbNode, (<JsxClosingElement>node).tagName);
|
||||
|
||||
case SyntaxKind.OptionalType:
|
||||
case SyntaxKind.RestType:
|
||||
case SyntaxKind.JSDocTypeExpression:
|
||||
case SyntaxKind.JSDocNonNullableType:
|
||||
case SyntaxKind.JSDocNullableType:
|
||||
case SyntaxKind.JSDocOptionalType:
|
||||
case SyntaxKind.JSDocVariadicType:
|
||||
return visitNode(cbNode, (<OptionalTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode>node).type);
|
||||
return visitNode(cbNode, (<OptionalTypeNode | RestTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode>node).type);
|
||||
case SyntaxKind.JSDocFunctionType:
|
||||
return visitNodes(cbNode, cbNodes, (<JSDocFunctionType>node).parameters) ||
|
||||
visitNode(cbNode, (<JSDocFunctionType>node).type);
|
||||
@ -2775,6 +2776,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseTupleElementType() {
|
||||
const pos = getNodePos();
|
||||
if (parseOptional(SyntaxKind.DotDotDotToken)) {
|
||||
const node = <RestTypeNode>createNode(SyntaxKind.RestType, pos);
|
||||
node.type = parseType();
|
||||
return finishNode(node);
|
||||
}
|
||||
const type = parseType();
|
||||
if (!(contextFlags & NodeFlags.JSDoc) && type.kind === SyntaxKind.JSDocNullableType && type.pos === (<JSDocNullableType>type).type.pos) {
|
||||
type.kind = SyntaxKind.OptionalType;
|
||||
|
||||
@ -226,6 +226,7 @@ namespace ts {
|
||||
ArrayType,
|
||||
TupleType,
|
||||
OptionalType,
|
||||
RestType,
|
||||
UnionType,
|
||||
IntersectionType,
|
||||
ConditionalType,
|
||||
@ -1107,6 +1108,11 @@ namespace ts {
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export interface RestTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.RestType;
|
||||
type: TypeNode;
|
||||
}
|
||||
|
||||
export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode;
|
||||
|
||||
export interface UnionTypeNode extends TypeNode {
|
||||
@ -3854,6 +3860,7 @@ namespace ts {
|
||||
|
||||
export interface TupleType extends GenericType {
|
||||
minLength: number;
|
||||
hasRestElement: boolean;
|
||||
associatedNames?: __String[];
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user