mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Add support for Inverse Offset and Range types
This commit is contained in:
parent
2e97918d43
commit
c03b2eed76
@ -673,6 +673,8 @@ namespace ts {
|
||||
const literalTypes = createMap<LiteralType>();
|
||||
const indexedAccessTypes = createMap<IndexedAccessType>();
|
||||
const substitutionTypes = createMap<SubstitutionType>();
|
||||
const rangeTypes = createMap<RangeType>();
|
||||
const inverseOffsetTypes = createMap<InverseOffsetType>();
|
||||
const evolvingArrayTypes: EvolvingArrayType[] = [];
|
||||
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
|
||||
|
||||
@ -721,6 +723,7 @@ namespace ts {
|
||||
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
|
||||
const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]);
|
||||
const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType;
|
||||
const offsetConstraintType = getUnionType([stringType, numberType]);
|
||||
const numberOrBigIntType = getUnionType([numberType, bigintType]);
|
||||
|
||||
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
|
||||
@ -876,6 +879,10 @@ namespace ts {
|
||||
const zeroType = getLiteralType(0);
|
||||
const zeroBigIntType = getLiteralType({ negative: false, base10Value: "0" });
|
||||
|
||||
const boundarySymbol = createSymbol(SymbolFlags.None, InternalSymbolName.Boundary);
|
||||
const lowerBoundType = createLiteralType(TypeFlags.NumberLiteral, 0, boundarySymbol);
|
||||
const upperBoundType = getInverseOffsetType(lowerBoundType);
|
||||
|
||||
const resolutionTargets: TypeSystemEntity[] = [];
|
||||
const resolutionResults: boolean[] = [];
|
||||
const resolutionPropertyNames: TypeSystemPropertyName[] = [];
|
||||
@ -982,6 +989,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
|
||||
if (message === Diagnostics.Type_0_cannot_be_used_to_index_type_1) debugger;
|
||||
const diagnostic = location
|
||||
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
|
||||
: createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
|
||||
@ -4218,6 +4226,12 @@ namespace ts {
|
||||
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
|
||||
return createTypeOperatorNode(indexTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.InverseOffset) {
|
||||
const indexType = (<InverseOffsetType>type).indexType;
|
||||
context.approximateLength += 1;
|
||||
const indexTypeNode = typeToTypeNodeHelper(indexType, context);
|
||||
return createInverseOffsetTypeNode(indexTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.IndexedAccess) {
|
||||
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
|
||||
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
|
||||
@ -4235,6 +4249,12 @@ namespace ts {
|
||||
context.approximateLength += 15;
|
||||
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.Range) {
|
||||
const objectTypeNode = typeToTypeNodeHelper((<RangeType>type).objectType, context);
|
||||
const startTypeNode = (<RangeType>type).startType !== lowerBoundType ? typeToTypeNodeHelper((<RangeType>type).startType, context) : undefined;
|
||||
const endTypeNode = (<RangeType>type).endType !== upperBoundType ? typeToTypeNodeHelper((<RangeType>type).endType, context) : undefined;
|
||||
return createRangeTypeNode(objectTypeNode, startTypeNode, endTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.Substitution) {
|
||||
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
|
||||
}
|
||||
@ -9691,13 +9711,13 @@ namespace ts {
|
||||
else if ((<ObjectType>type).objectFlags & ObjectFlags.ClassOrInterface) {
|
||||
resolveClassOrInterfaceMembers(<InterfaceType>type);
|
||||
}
|
||||
else if ((<ReverseMappedType>type).objectFlags & ObjectFlags.ReverseMapped) {
|
||||
else if ((<ObjectType>type).objectFlags & ObjectFlags.ReverseMapped) {
|
||||
resolveReverseMappedTypeMembers(type as ReverseMappedType);
|
||||
}
|
||||
else if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
|
||||
resolveAnonymousTypeMembers(<AnonymousType>type);
|
||||
}
|
||||
else if ((<MappedType>type).objectFlags & ObjectFlags.Mapped) {
|
||||
else if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
|
||||
resolveMappedTypeMembers(<MappedType>type);
|
||||
}
|
||||
}
|
||||
@ -10007,6 +10027,9 @@ namespace ts {
|
||||
if (t.flags & TypeFlags.Index) {
|
||||
return keyofConstraintType;
|
||||
}
|
||||
if (t.flags & TypeFlags.InverseOffset) {
|
||||
return offsetConstraintType;
|
||||
}
|
||||
if (t.flags & TypeFlags.IndexedAccess) {
|
||||
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
|
||||
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
|
||||
@ -10020,6 +10043,13 @@ namespace ts {
|
||||
constraintDepth--;
|
||||
return result;
|
||||
}
|
||||
if (t.flags & TypeFlags.Range) {
|
||||
const baseObjectType = getBaseConstraint((<RangeType>t).objectType);
|
||||
const baseStartType = getBaseConstraint((<RangeType>t).startType);
|
||||
const baseEndType = getBaseConstraint((<RangeType>t).endType);
|
||||
const baseRangeType = baseObjectType && baseStartType && baseEndType && getRangeTypeOrUndefined(baseObjectType, baseStartType, baseEndType);
|
||||
return baseRangeType && getBaseConstraint(baseRangeType);
|
||||
}
|
||||
if (t.flags & TypeFlags.Substitution) {
|
||||
return getBaseConstraint((<SubstitutionType>t).substitute);
|
||||
}
|
||||
@ -12218,6 +12248,7 @@ namespace ts {
|
||||
type === wildcardType ? wildcardType :
|
||||
type.flags & TypeFlags.Unknown ? neverType :
|
||||
type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
|
||||
type.flags & TypeFlags.InverseOffset ? getIndexType(offsetConstraintType, stringsOnly, noIndexSignatures) :
|
||||
stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral) :
|
||||
!noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol)]) :
|
||||
getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
|
||||
@ -12346,6 +12377,7 @@ namespace ts {
|
||||
if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
const stringIndexInfo = getIndexInfoOfType(objectType, IndexKind.String);
|
||||
const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) || stringIndexInfo;
|
||||
if (indexInfo) {
|
||||
@ -12462,7 +12494,18 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isGenericIndexType(type: Type): boolean {
|
||||
return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.Index);
|
||||
if (maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.Index)) {
|
||||
return true;
|
||||
}
|
||||
if (type.flags & TypeFlags.UnionOrIntersection) {
|
||||
for (const t of (type as UnionOrIntersectionType).types) {
|
||||
if (t.flags & TypeFlags.InverseOffset && isGenericIndexType((t as InverseOffsetType).indexType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return !!(type.flags & TypeFlags.InverseOffset) && isGenericIndexType((type as InverseOffsetType).indexType);
|
||||
}
|
||||
|
||||
function isThisTypeParameter(type: Type): boolean {
|
||||
@ -12471,6 +12514,7 @@ namespace ts {
|
||||
|
||||
function getSimplifiedType(type: Type, writing: boolean): Type {
|
||||
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(<IndexedAccessType>type, writing) :
|
||||
type.flags & TypeFlags.Range ? getSimplifiedRangeType(<RangeType>type, writing) :
|
||||
type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(<ConditionalType>type, writing) :
|
||||
type;
|
||||
}
|
||||
@ -12542,6 +12586,65 @@ namespace ts {
|
||||
return type[cache] = type;
|
||||
}
|
||||
|
||||
function distributeRangeOverObjectType(objectType: Type, startType: Type, endType: Type, writing: boolean) {
|
||||
// (T | U)[X:Y] -> T[X:Y] | U[X:Y]
|
||||
// (T & U)[X:Y] -> T[X:Y] & U[X:Y]
|
||||
if (objectType.flags & TypeFlags.UnionOrIntersection) {
|
||||
const types = map((objectType as UnionOrIntersectionType).types, t => getSimplifiedType(getRangeType(t, startType, endType), writing));
|
||||
return objectType.flags & TypeFlags.Intersection ? getIntersectionType(types) : getUnionType(types);
|
||||
}
|
||||
}
|
||||
|
||||
function distributeObjectOverRangeTypes(objectType: Type, startType: Type, endType: Type, writing: boolean) {
|
||||
// T[A|B:C|D] -> T[A:C] | T[A:D] | T[B:C] | T[B:D]
|
||||
if (startType.flags & TypeFlags.Union || endType.flags & TypeFlags.Union) {
|
||||
const types: Type[] = [];
|
||||
for (const start of getConstituents(startType)) {
|
||||
for (const end of getConstituents(endType)) {
|
||||
types.push(getSimplifiedType(getRangeType(objectType, start, end), writing));
|
||||
}
|
||||
}
|
||||
return getUnionType(types);
|
||||
}
|
||||
}
|
||||
|
||||
function getSimplifiedRangeType(type: RangeType, writing: boolean) {
|
||||
const cache = writing ? "simplifiedForWriting" : "simplifiedForReading";
|
||||
if (type[cache]) {
|
||||
return type[cache] === circularConstraintType ? type : type[cache]!;
|
||||
}
|
||||
type[cache] = circularConstraintType;
|
||||
// We recursively simplify the object type as it may in turn be a range type. For example, with
|
||||
// '{ [P in T]: { [Q in U]: number } }[T:][U:]' we want to first simplify the inner range type.
|
||||
const objectType = unwrapSubstitution(getSimplifiedType(type.objectType, writing));
|
||||
const startType = getSimplifiedType(type.startType, writing);
|
||||
const endType = getSimplifiedType(type.endType, writing);
|
||||
|
||||
// T[A|B:C|D] -> T[A:C] | T[A:D] | T[B:C] | T[B:D]
|
||||
const distributedOverStartEnd = distributeObjectOverRangeTypes(objectType, startType, endType, writing);
|
||||
if (distributedOverStartEnd) {
|
||||
return type[cache] = distributedOverStartEnd;
|
||||
}
|
||||
|
||||
// Only do the inner distribution if the start/end offsets can no longer be instantiated to cause distribution again
|
||||
if (!(startType.flags & TypeFlags.Instantiable) && !(endType.flags & TypeFlags.Instantiable)) {
|
||||
// (T | U)[A:B] -> T[A:B] | U[A:B]
|
||||
// (T & U)[A:B] -> T[A:B] & U[A:B]
|
||||
const distributedOverObject = distributeRangeOverObjectType(objectType, startType, endType, writing);
|
||||
if (distributedOverObject) {
|
||||
return type[cache] = distributedOverObject;
|
||||
}
|
||||
}
|
||||
|
||||
// So ultimately:
|
||||
// ((A & B) | C)[K1 | K2:K3 | K4]
|
||||
// -> ((A & B) | C)[K1:K3 | K4] | ((A & B) | C)[K2:K3 | K4]
|
||||
// -> ((A & B) | C)[K1:K3] | ((A & B) | C)[K1:K4] | ((A & B) | C)[K2:K4] | ((A & B) | C)[K2:K4]
|
||||
// -> (A & B)[K1:K3] | C[K1:K3] | (A & B)[K1:K4] | C[K1:K4] | (A & B)[K2:K3] | C[K2:K3] | (A & B)[K2:K4] | C[K2:K4]
|
||||
// -> (A[K1:K3] & B[K1:K3]) | C[K1:K3] | (A[K1:K4] & B[K1:K4]) | C[K1:K4] | (A[K2:K3] & B[K2:K3]) | C[K2:K3] | (A[K2:K4] & B[K2:K4]) | C[K2:K4]
|
||||
return type[cache] = type;
|
||||
}
|
||||
|
||||
function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) {
|
||||
const checkType = type.checkType;
|
||||
const extendsType = type.extendsType;
|
||||
@ -12580,6 +12683,71 @@ namespace ts {
|
||||
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
|
||||
}
|
||||
|
||||
function getInvertedIndexType(indexType: Type, objectType: Type, errorNode: Node | undefined) {
|
||||
if (indexType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Number | TypeFlags.String)) {
|
||||
return indexType;
|
||||
}
|
||||
let index = getNumericIndexFromIndexType(indexType);
|
||||
if (index !== undefined) {
|
||||
if (isArrayType(objectType)) {
|
||||
return numberType;
|
||||
}
|
||||
if (isTupleType(objectType)) {
|
||||
if (index < 0) {
|
||||
return getLiteralType(-index);
|
||||
}
|
||||
const length = getLengthOfTupleType(objectType);
|
||||
index = length - index;
|
||||
if (index < 0) index = 0;
|
||||
if (objectType.target.hasRestElement) {
|
||||
if (index < length) {
|
||||
const indexTypes: Type[] = [];
|
||||
for (let i = index; i <= length; i++) {
|
||||
indexTypes.push(getLiteralType(i));
|
||||
}
|
||||
return getUnionType(indexTypes);
|
||||
}
|
||||
}
|
||||
return getLiteralType(index);
|
||||
}
|
||||
}
|
||||
if (errorNode) {
|
||||
error(errorNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
|
||||
}
|
||||
}
|
||||
|
||||
function resolveInverseOffsets(indexType: Type, objectType: Type, errorNode: Node | undefined) {
|
||||
if (indexType.flags & TypeFlags.Union) {
|
||||
let indexTypes: Type[] | undefined;
|
||||
let hasErrors = false;
|
||||
const types = (<UnionType>indexType).types;
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
const t = types[i];
|
||||
if (t.flags & TypeFlags.InverseOffset) {
|
||||
if (!indexTypes) indexTypes = types.slice(0, i);
|
||||
const inverted = getInvertedIndexType((<InverseOffsetType>t).indexType, objectType, errorNode);
|
||||
if (inverted) {
|
||||
indexTypes.push(inverted);
|
||||
}
|
||||
else if (!errorNode) {
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
else if (indexTypes) {
|
||||
indexTypes.push(t);
|
||||
}
|
||||
}
|
||||
return hasErrors ? undefined : indexTypes ? getUnionType(indexTypes) : indexType;
|
||||
}
|
||||
if (indexType.flags & TypeFlags.InverseOffset) {
|
||||
return getInvertedIndexType((<InverseOffsetType>indexType).indexType, objectType, errorNode);
|
||||
}
|
||||
return indexType;
|
||||
}
|
||||
|
||||
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression): Type {
|
||||
return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, AccessFlags.None) || (accessNode ? errorType : unknownType);
|
||||
}
|
||||
@ -12603,13 +12771,18 @@ namespace ts {
|
||||
return objectType;
|
||||
}
|
||||
// Defer the operation by creating an indexed access type.
|
||||
const id = objectType.id + "," + indexType.id;
|
||||
const id = `${objectType.id},${indexType.id}`;
|
||||
let type = indexedAccessTypes.get(id);
|
||||
if (!type) {
|
||||
indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
const resolvedIndexType = resolveInverseOffsets(indexType, objectType, accessNode);
|
||||
if (!resolvedIndexType) {
|
||||
return undefined;
|
||||
}
|
||||
indexType = resolvedIndexType;
|
||||
// In the following we resolve T[K] to the type of the property in T selected by K.
|
||||
// We treat boolean as different from other unions to improve errors;
|
||||
// skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'.
|
||||
@ -12668,6 +12841,315 @@ namespace ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getRangeType(objectType: Type, startType: Type, endType: Type, rangeNode?: RangeTypeNode, accessFlags?: AccessFlags): Type {
|
||||
return getRangeTypeOrUndefined(objectType, startType, endType, rangeNode, accessFlags) ?? (rangeNode ? errorType : unknownType);
|
||||
}
|
||||
|
||||
function getRangeTypeOrUndefined(objectType: Type, startType: Type, endType: Type, rangeNode?: RangeTypeNode, accessFlags = AccessFlags.None): Type | undefined {
|
||||
if (objectType === wildcardType || startType === wildcardType || endType === wildcardType) {
|
||||
return wildcardType;
|
||||
}
|
||||
// The object type is constrained to be an array or tuple type
|
||||
if (!isTypeAssignableTo(objectType, anyReadonlyArrayType)) {
|
||||
if (rangeNode) {
|
||||
error(rangeNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(objectType));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// The start type is constrained to be a string- or number-like type
|
||||
if (!isTypeAssignableTo(startType, offsetConstraintType)) {
|
||||
if (rangeNode?.startType) {
|
||||
error(rangeNode.startType, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(startType), typeToString(objectType));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// The end type is constrained to be a string- or number-like type
|
||||
if (!isTypeAssignableTo(endType, offsetConstraintType)) {
|
||||
if (rangeNode?.endType) {
|
||||
error(rangeNode.endType, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(endType), typeToString(objectType));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// If the object, start, or end type is generic, we are performing a higher-order slice, and the operation
|
||||
// is deferred until it can be instantiated.
|
||||
if (isGenericObjectType(objectType) || isGenericIndexType(startType) || isGenericIndexType(endType)) {
|
||||
// A generic start or end on 'any' or 'unkown' is 'any' or 'unknown' (respectively).
|
||||
if (objectType.flags & TypeFlags.AnyOrUnknown) {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
// If we can eagerly resolve start and end and they indicate the range is either
|
||||
// all inclusive or empty, we return the object type or an empty tuple (respectively).
|
||||
let start = getNumericIndexFromIndexType(startType);
|
||||
let end = getNumericIndexFromIndexType(endType);
|
||||
if (start !== undefined && end !== undefined) {
|
||||
// A range consisting of the entire array is just the array:
|
||||
// T[:] -> T
|
||||
// T[0:^0] -> T
|
||||
if (start === 0 && end === ~0) {
|
||||
return objectType;
|
||||
}
|
||||
// A range that starts at the upper bound (^0), or ends at the lower bound (0), is just an empty tuple:
|
||||
// T[^0:] -> []
|
||||
// T[:0] -> []
|
||||
// A range that starts at or after it ends is just an empty tuple:
|
||||
// T[2:1] -> []
|
||||
// T[1:1] -> []
|
||||
// T[^1:^2] -> []
|
||||
// T[^1:^1] -> []
|
||||
if (start === ~0 || end === 0 ||
|
||||
(start < 0 === end < 0) && start >= end) {
|
||||
return createTupleType(emptyArray, 0, /*hasRestElement*/ false, !isMutableArrayOrTuple(objectType));
|
||||
}
|
||||
}
|
||||
|
||||
// Defer the operation until it can be fully instantiated.
|
||||
const id = `${objectType.id},${startType.id},${endType.id}`;
|
||||
let type = rangeTypes.get(id);
|
||||
if (!type) {
|
||||
type = <RangeType>createType(TypeFlags.Range);
|
||||
type.objectType = objectType;
|
||||
type.startType = startType;
|
||||
type.endType = endType;
|
||||
rangeTypes.set(id, type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
if (objectType.flags & TypeFlags.Union || startType.flags & TypeFlags.Union || endType.flags & TypeFlags.Union) {
|
||||
const results: Type[] = [];
|
||||
let hasErrors = false;
|
||||
for (const object of getConstituents(objectType)) {
|
||||
for (const start of getConstituents(startType)) {
|
||||
for (const end of getConstituents(endType)) {
|
||||
const type = getTupleSliceFromRangeType(object, objectType, start, end, rangeNode);
|
||||
if (type) {
|
||||
results.push(type);
|
||||
}
|
||||
else if (!rangeNode) {
|
||||
// If there's no error node, we can immediately stop, since error reporting is off
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
// Otherwise we set a flag and return at the end of the loop so we still mark all errors
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasErrors) {
|
||||
return undefined;
|
||||
}
|
||||
return accessFlags & AccessFlags.Writing ? getIntersectionType(results) : getUnionType(results);
|
||||
}
|
||||
return getTupleSliceFromRangeType(objectType, objectType, startType, endType, rangeNode);
|
||||
}
|
||||
|
||||
function getTupleSliceFromRangeType(objectType: Type, fullObjectType: Type, startType: Type, endType: Type, rangeNode: RangeTypeNode | undefined): Type | undefined {
|
||||
Debug.assert(!(objectType.flags & TypeFlags.Union));
|
||||
Debug.assert(!(startType.flags & TypeFlags.Union));
|
||||
Debug.assert(!(endType.flags & TypeFlags.Union));
|
||||
|
||||
// If the start type is `string`, `number`, `any`, or `never`, the result is an array
|
||||
// of a union of all element types from the lower bound (0) to end.
|
||||
//
|
||||
// If the end type is `string`, `number`, `any`, or `never`, the result is an array
|
||||
// of a union of all element types from start to the upper bound (^0).
|
||||
//
|
||||
// T[number:number] -> T[number][]
|
||||
// T[number:Y] -> T[0:Y][number][]
|
||||
// T[X:number] -> T[X:^0][number][]
|
||||
const startIndexType = startType.flags & TypeFlags.InverseOffset ? (<InverseOffsetType>startType).indexType : startType;
|
||||
const endIndexType = endType.flags & TypeFlags.InverseOffset ? (<InverseOffsetType>endType).indexType : endType;
|
||||
const hasNonLiteralStart = startIndexType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Number | TypeFlags.String);
|
||||
const hasNonLiteralEnd = endIndexType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Number | TypeFlags.String);
|
||||
if (hasNonLiteralStart && hasNonLiteralEnd) {
|
||||
const accessType = getIndexedAccessType(objectType, numberType);
|
||||
return createArrayType(accessType, !isMutableArrayOrTuple(objectType));
|
||||
}
|
||||
if (hasNonLiteralStart) {
|
||||
const limitedRange = getTupleSliceFromRangeType(objectType, fullObjectType, lowerBoundType, endType, rangeNode);
|
||||
if (!limitedRange) {
|
||||
return undefined;
|
||||
}
|
||||
const accessType = getIndexedAccessType(limitedRange, numberType);
|
||||
return createArrayType(accessType, !isMutableArrayOrTuple(objectType));
|
||||
}
|
||||
if (hasNonLiteralEnd) {
|
||||
const limitedRange = getTupleSliceFromRangeType(objectType, fullObjectType, startType, upperBoundType, rangeNode);
|
||||
if (!limitedRange) {
|
||||
return undefined;
|
||||
}
|
||||
const accessType = getIndexedAccessType(limitedRange, numberType);
|
||||
return createArrayType(accessType, !isMutableArrayOrTuple(objectType));
|
||||
}
|
||||
|
||||
let start = getNumericIndexFromIndexType(startType);
|
||||
if (start === undefined) {
|
||||
if (rangeNode) {
|
||||
error(rangeNode.startType ?? rangeNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(startType), typeToString(fullObjectType));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let end = getNumericIndexFromIndexType(endType);
|
||||
if (end === undefined) {
|
||||
if (rangeNode) {
|
||||
error(rangeNode.endType ?? rangeNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(endType), typeToString(fullObjectType));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// A range consisting of the entire array is just the array:
|
||||
// T[:] -> T
|
||||
// T[0:^0] -> T
|
||||
if (start === 0 && end === ~0) {
|
||||
return objectType;
|
||||
}
|
||||
|
||||
// A range that starts at the upper bound (`^0`) or ends at the lower bound (`0`) is just an empty tuple:
|
||||
// T[^0:] -> []
|
||||
// T[:0] -> []
|
||||
// A range that starts at or after it ends is just an empty tuple:
|
||||
// T[2:1] -> []
|
||||
// T[1:1] -> []
|
||||
// T[^1:^2] -> []
|
||||
// T[^1:^1] -> []
|
||||
if (start === ~0 || end === 0 ||
|
||||
(start < 0 === end < 0) && start >= end) {
|
||||
return createTupleType(emptyArray, 0, /*hasRestElement*/ false, !isMutableArrayOrTuple(objectType));
|
||||
}
|
||||
|
||||
const elementTypes: Type[] = [];
|
||||
let minLength = 0;
|
||||
let hasRestElement = false;
|
||||
let associatedNames: __String[] | undefined;
|
||||
if (!isTupleType(objectType)) {
|
||||
if (start < 0 === end < 0) {
|
||||
// If both signs agree, we can collect a tuple of a fixed length:
|
||||
// T[][0:1] -> [T?]
|
||||
// T[][^1:^0] -> [T?]
|
||||
const elementType = getElementTypeOfArrayType(objectType);
|
||||
Debug.assert(elementType !== undefined);
|
||||
const length = end - start;
|
||||
for (let i = 0; i < length; i++) {
|
||||
elementTypes.push(elementType);
|
||||
}
|
||||
}
|
||||
else if (start < 0 && end > 0) {
|
||||
// If start is inverse and end is not, we can create a tuple of min(abs(start), end) optional elements.
|
||||
const elementType = getElementTypeOfArrayType(objectType);
|
||||
Debug.assert(elementType !== undefined);
|
||||
const length = Math.min(end, ~start);
|
||||
for (let i = 0; i < length; i++) {
|
||||
elementTypes.push(elementType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the signs disagree, we cannot determine the lower and upper bound, thus we should return the entire array:
|
||||
// T[][0:^1] -> T[]
|
||||
// T[][^1:10] -> T[]
|
||||
return objectType;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const length = getLengthOfTupleType(objectType);
|
||||
if (objectType.target.hasRestElement) {
|
||||
if (start < 0) {
|
||||
// If our start position is inverted, the result is an array of all of the optional or rest element types
|
||||
// along with the set of required element types starting from the right equal to start:
|
||||
// [A, B, ...C[]][^0:] -> C[]
|
||||
// [A, B, ...C[]][^1:] -> (B | C)[]
|
||||
// [A, B, ...C[]][^2:] -> (A | B | C)[]
|
||||
const types: Type[] = [];
|
||||
for (let i = objectType.target.minLength; i <= length; i++) {
|
||||
const elementType = getTupleElementType(objectType, i);
|
||||
Debug.assert(elementType !== undefined);
|
||||
types.push(elementType);
|
||||
}
|
||||
for (let i = 0; i < ~start && i < objectType.target.minLength; i++) {
|
||||
const elementType = getTupleElementType(objectType, objectType.target.minLength - i);
|
||||
Debug.assert(elementType !== undefined);
|
||||
types.push(elementType);
|
||||
}
|
||||
return createArrayType(getUnionType(types), !isMutableArrayOrTuple(objectType));
|
||||
}
|
||||
if (end < 0) {
|
||||
// If our end position is inverted, the result is a tuple whose minimum length
|
||||
// is reduced by the inverted amount:
|
||||
// [A, B, C, ...D[]][1:^0] -> [B, C, ...D[]]
|
||||
// [A, B, C, ...D[]][1:^1] -> [B, C?, ...D[]]
|
||||
// [A, B, C, ...D[]][1:^2] -> [B?, C?, ...D[]]
|
||||
hasRestElement = true;
|
||||
minLength = -Math.min(~end, length);
|
||||
end = length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If we do not have a rest element, then we can explicitly adjust inverted offsets
|
||||
// relative to the length of the tuple:
|
||||
// [A, B, C][:^2] -> [A, B, C][:1] -> [A]
|
||||
// [A, B, C][^2:] -> [A, B, C][1:] -> [B, C]
|
||||
if (start < 0) start = Math.max(0, length - ~start);
|
||||
if (end < 0) end = Math.max(0, length - ~end);
|
||||
// We also clamp start and end to the length of the fixed tuple:
|
||||
// [A, B][1:3] -> [A, B][1:2] -> [B]
|
||||
if (start > length) start = length;
|
||||
if (end > length) end = length;
|
||||
}
|
||||
|
||||
// At this point our offsets should no longer be inverted.
|
||||
Debug.assert(start >= 0 && end >= 0);
|
||||
|
||||
if (objectType.target.associatedNames) {
|
||||
associatedNames = [];
|
||||
}
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
if (i < objectType.target.minLength) minLength++;
|
||||
const elementType = getTupleElementType(objectType, i);
|
||||
Debug.assert(elementType !== undefined);
|
||||
elementTypes.push(elementType);
|
||||
if (objectType.target.associatedNames && associatedNames && i < objectType.target.associatedNames.length) {
|
||||
associatedNames.push(objectType.target.associatedNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (minLength < 0) minLength = 0;
|
||||
|
||||
if (hasRestElement) {
|
||||
const restType = getTupleElementType(objectType, end);
|
||||
Debug.assert(restType !== undefined);
|
||||
if (objectType.target.associatedNames && associatedNames && end < objectType.target.associatedNames.length) {
|
||||
associatedNames.push(objectType.target.associatedNames[end]);
|
||||
}
|
||||
elementTypes.push(restType);
|
||||
}
|
||||
}
|
||||
|
||||
return createTupleType(elementTypes, minLength, hasRestElement, !isMutableArrayOrTuple(objectType), associatedNames);
|
||||
}
|
||||
|
||||
function getTypeFromRangeTypeNode(node: RangeTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
const objectType = getTypeFromTypeNode(node.objectType);
|
||||
// A missing start is the lower bound (0):
|
||||
// T[:Y] -> T[0:Y]
|
||||
const startType = node.startType ? getTypeFromTypeNode(node.startType) : lowerBoundType;
|
||||
// A missing end is the upper bound (^0):
|
||||
// T[X:] -> T[X:^0]
|
||||
const endOffset = node.endType ? getTypeFromTypeNode(node.endType) : upperBoundType;
|
||||
const resolved = getRangeType(objectType, startType, endOffset, node);
|
||||
links.resolvedType = resolved.flags & TypeFlags.Range &&
|
||||
(<RangeType>resolved).objectType === objectType &&
|
||||
(<RangeType>resolved).startType === startType &&
|
||||
(<RangeType>resolved).endType === endOffset ?
|
||||
getConstrainedTypeVariable(<RangeType>resolved, node) :
|
||||
resolved;
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getActualTypeVariable(type: Type): Type {
|
||||
if (type.flags & TypeFlags.Substitution) {
|
||||
return (<SubstitutionType>type).typeVariable;
|
||||
@ -12677,6 +13159,15 @@ namespace ts {
|
||||
(<IndexedAccessType>type).indexType.flags & TypeFlags.Substitution)) {
|
||||
return getIndexedAccessType(getActualTypeVariable((<IndexedAccessType>type).objectType), getActualTypeVariable((<IndexedAccessType>type).indexType));
|
||||
}
|
||||
if (type.flags & TypeFlags.Range && (
|
||||
(<RangeType>type).objectType.flags & TypeFlags.Substitution ||
|
||||
(<RangeType>type).startType.flags & TypeFlags.Substitution ||
|
||||
(<RangeType>type).endType.flags & TypeFlags.Substitution)) {
|
||||
return getRangeType(
|
||||
getActualTypeVariable((<RangeType>type).objectType),
|
||||
getActualTypeVariable((<RangeType>type).startType),
|
||||
getActualTypeVariable((<RangeType>type).endType));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -12804,6 +13295,71 @@ namespace ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if `type` is a string or numeric literal type for an numeric index. If `type` was
|
||||
* an inverted offset, the twos-complement of the index is returned.
|
||||
* @param invertNegativeOffsets Indicates whether negative offsets from a non-inverted offset type
|
||||
* should be treated as an inverted offset (default `true`). For an indexed access type, negative offsets should not
|
||||
* be inverted:
|
||||
* ```
|
||||
* // indexed access
|
||||
* T[-1] -> T["-1"]
|
||||
*
|
||||
* // range
|
||||
* T[-1:] -> T[^1]
|
||||
* ```
|
||||
*/
|
||||
function getNumericIndexFromIndexType(type: Type, invertNegativeOffsets = true) {
|
||||
const indexType = type.flags & TypeFlags.InverseOffset ? (type as InverseOffsetType).indexType : type;
|
||||
let index =
|
||||
isNumericLiteralType(indexType) ? indexType.value :
|
||||
isStringLiteralType(indexType) ? canonicalNumericIndex(indexType.value) :
|
||||
undefined;
|
||||
if (index === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (!isInteger(index)) {
|
||||
return undefined;
|
||||
}
|
||||
let inverted = indexType !== type;
|
||||
if (index < 0 && (inverted || invertNegativeOffsets)) {
|
||||
index = -index;
|
||||
inverted = !inverted;
|
||||
}
|
||||
return inverted ? ~index : index;
|
||||
}
|
||||
|
||||
function getInverseOffsetType(indexType: Type): Type {
|
||||
if (indexType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Number | TypeFlags.String)) {
|
||||
return indexType;
|
||||
}
|
||||
// The inverse offset of an inverse offset is the normal offset.
|
||||
if (indexType.flags & TypeFlags.InverseOffset) {
|
||||
return (<InverseOffsetType>indexType).indexType;
|
||||
}
|
||||
if (indexType.flags & TypeFlags.Union) {
|
||||
return mapType(indexType, getInverseOffsetType);
|
||||
}
|
||||
// An inverse offset cannot be otherwise resolved without some context object from which to derive
|
||||
// the length.
|
||||
const id = "" + indexType.id;
|
||||
let type = inverseOffsetTypes.get(id);
|
||||
if (!type) {
|
||||
type = <InverseOffsetType>createType(TypeFlags.InverseOffset);
|
||||
type.indexType = indexType;
|
||||
inverseOffsetTypes.set(id, type);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function getTypeFromInverseOffsetTypeNode(node: InverseOffsetTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = getInverseOffsetType(getTypeFromTypeNode(node.indexType));
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getIdentifierChain(node: EntityName): Identifier[] {
|
||||
if (isIdentifier(node)) {
|
||||
return [node];
|
||||
@ -13292,10 +13848,14 @@ namespace ts {
|
||||
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
|
||||
case SyntaxKind.MappedType:
|
||||
return getTypeFromMappedTypeNode(<MappedTypeNode>node);
|
||||
case SyntaxKind.RangeType:
|
||||
return getTypeFromRangeTypeNode(<RangeTypeNode>node);
|
||||
case SyntaxKind.ConditionalType:
|
||||
return getTypeFromConditionalTypeNode(<ConditionalTypeNode>node);
|
||||
case SyntaxKind.InferType:
|
||||
return getTypeFromInferTypeNode(<InferTypeNode>node);
|
||||
case SyntaxKind.InverseOffsetType:
|
||||
return getTypeFromInverseOffsetTypeNode(<InverseOffsetTypeNode>node);
|
||||
case SyntaxKind.ImportType:
|
||||
return getTypeFromImportTypeNode(<ImportTypeNode>node);
|
||||
// This function assumes that an identifier or qualified name is a type expression
|
||||
@ -13749,6 +14309,15 @@ namespace ts {
|
||||
if (flags & TypeFlags.Conditional) {
|
||||
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
|
||||
}
|
||||
if (flags & TypeFlags.InverseOffset) {
|
||||
return getInverseOffsetType(instantiateType((<InverseOffsetType>type).indexType, mapper));
|
||||
}
|
||||
if (flags & TypeFlags.Range) {
|
||||
return getRangeType(
|
||||
instantiateType((<RangeType>type).objectType, mapper),
|
||||
instantiateType((<RangeType>type).startType, mapper),
|
||||
instantiateType((<RangeType>type).endType, mapper));
|
||||
}
|
||||
if (flags & TypeFlags.Substitution) {
|
||||
const maybeVariable = instantiateType((<SubstitutionType>type).typeVariable, mapper);
|
||||
if (maybeVariable.flags & TypeFlags.TypeVariable) {
|
||||
@ -15545,6 +16114,9 @@ namespace ts {
|
||||
if (flags & TypeFlags.Index) {
|
||||
return isRelatedTo((<IndexType>source).type, (<IndexType>target).type, /*reportErrors*/ false);
|
||||
}
|
||||
if (flags & TypeFlags.InverseOffset) {
|
||||
return isRelatedTo((<InverseOffsetType>source).indexType, (<InverseOffsetType>target).indexType, /*reportErrors*/ false);
|
||||
}
|
||||
let result = Ternary.False;
|
||||
if (flags & TypeFlags.IndexedAccess) {
|
||||
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, /*reportErrors*/ false)) {
|
||||
@ -15642,6 +16214,39 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (target.flags & TypeFlags.InverseOffset) {
|
||||
// A ^S is related to a ^T if S is related to T
|
||||
if (source.flags & TypeFlags.InverseOffset) {
|
||||
if (result = isRelatedTo((<InverseOffsetType>source).indexType, (<InverseOffsetType>target).indexType, /*reportErrors*/ false)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// TODO(rbuckton): Are they any other possible assignability relations?
|
||||
}
|
||||
else if (target.flags & TypeFlags.Range) {
|
||||
// A type S is related to a type T[X:Y] if S is related to C, where C is the base
|
||||
// constraint of T[X:Y] for writing.
|
||||
if (relation !== identityRelation) {
|
||||
const objectType = (<RangeType>target).objectType;
|
||||
const startType = (<RangeType>target).startType;
|
||||
const endType = (<RangeType>target).endType;
|
||||
const baseObjectType = getBaseConstraintOfType(objectType) ?? objectType;
|
||||
const baseStartType = getBaseConstraintOfType(startType) ?? startType;
|
||||
const baseEndType = getBaseConstraintOfType(endType) ?? endType;
|
||||
if (!isGenericObjectType(baseObjectType) && !isGenericIndexType(baseStartType) && !isGenericIndexType(baseEndType)) {
|
||||
const accessFlags = AccessFlags.Writing | (baseObjectType !== objectType ? AccessFlags.NoIndexSignatures : 0);
|
||||
const constraint = getRangeTypeOrUndefined(
|
||||
baseObjectType,
|
||||
baseStartType,
|
||||
baseEndType,
|
||||
/*rangeNode*/ undefined,
|
||||
accessFlags);
|
||||
if (constraint && (result = isRelatedTo(source, constraint, reportErrors))) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isGenericMappedType(target)) {
|
||||
// A source type T is related to a target type { [P in X]: T[P] }
|
||||
const template = getTemplateTypeFromMappedType(target);
|
||||
@ -15686,6 +16291,18 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (source.flags & TypeFlags.Range && target.flags & TypeFlags.Range) {
|
||||
// A type S[XS:YS] is related to a type T[XT:YT] if S is related to T, XS is related to XT, and YX is related to YT.
|
||||
if (result = isRelatedTo((<RangeType>source).objectType, (<RangeType>target).objectType, reportErrors)) {
|
||||
if (result &= isRelatedTo((<RangeType>source).startType, (<RangeType>target).startType, reportErrors)) {
|
||||
result &= isRelatedTo((<RangeType>source).endType, (<RangeType>target).endType, reportErrors);
|
||||
}
|
||||
}
|
||||
if (result) {
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const constraint = getConstraintOfType(<TypeVariable>source);
|
||||
if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
|
||||
@ -15713,6 +16330,12 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.InverseOffset) {
|
||||
if (result = isRelatedTo(numberType, target, reportErrors)) {
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Conditional) {
|
||||
if (target.flags & TypeFlags.Conditional) {
|
||||
// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
|
||||
@ -17038,6 +17661,14 @@ namespace ts {
|
||||
isUnitType(type);
|
||||
}
|
||||
|
||||
function isNumericLiteralType(type: Type): type is LiteralType & { value: number } {
|
||||
return !!(type.flags & TypeFlags.NumberLiteral);
|
||||
}
|
||||
|
||||
function isStringLiteralType(type: Type): type is LiteralType & { value: string } {
|
||||
return !!(type.flags & TypeFlags.StringLiteral);
|
||||
}
|
||||
|
||||
function getBaseTypeOfLiteralType(type: Type): Type {
|
||||
return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(<LiteralType>type) :
|
||||
type.flags & TypeFlags.StringLiteral ? stringType :
|
||||
@ -19071,6 +19702,10 @@ namespace ts {
|
||||
return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1;
|
||||
}
|
||||
|
||||
function getConstituents(type: Type) {
|
||||
return type.flags & TypeFlags.Union ? (type as UnionType).types : [type];
|
||||
}
|
||||
|
||||
// Apply a mapping function to a type and return the resulting type. If the source type
|
||||
// is a union type, the mapping function is applied to each constituent type and a union
|
||||
// of the resulting types is returned.
|
||||
@ -22285,7 +22920,7 @@ namespace ts {
|
||||
// Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
|
||||
// This is desired behavior, because when indexing with them as numeric entities, you are indexing
|
||||
// with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
|
||||
return (+name).toString() === name;
|
||||
return canonicalNumericIndex(name) !== undefined;
|
||||
}
|
||||
|
||||
function checkComputedPropertyName(node: ComputedPropertyName): Type {
|
||||
@ -29162,6 +29797,43 @@ namespace ts {
|
||||
checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node);
|
||||
}
|
||||
|
||||
function checkOffsetIndexType(offsetType: Type, offsetNode: InverseOffsetTypeNode) {
|
||||
if (!(offsetType.flags & TypeFlags.InverseOffset)) {
|
||||
return offsetType;
|
||||
}
|
||||
if (!checkTypeAssignableTo((<InverseOffsetType>offsetType).indexType, offsetConstraintType, offsetNode.indexType, Diagnostics.Type_0_does_not_satisfy_the_constraint_1)) {
|
||||
return errorType;
|
||||
}
|
||||
let hasErrors = false;
|
||||
forEachType((<InverseOffsetType>offsetType).indexType, t => {
|
||||
if (isLiteralType(t) && getNumericIndexFromIndexType(t) === undefined) {
|
||||
hasErrors = true;
|
||||
if (offsetNode) {
|
||||
error(offsetNode.indexType, Diagnostics.Type_0_cannot_be_used_as_an_index_type_in_an_offset_or_range_type, typeToString(t));
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (hasErrors) {
|
||||
return errorType;
|
||||
}
|
||||
return offsetType;
|
||||
}
|
||||
|
||||
function checkOffsetType(node: InverseOffsetTypeNode) {
|
||||
checkSourceElement(node.indexType);
|
||||
checkOffsetIndexType(getTypeFromInverseOffsetTypeNode(node), node);
|
||||
}
|
||||
|
||||
function checkRangeType(node: RangeTypeNode) {
|
||||
checkSourceElement(node.objectType);
|
||||
checkSourceElement(node.startType);
|
||||
checkSourceElement(node.endType);
|
||||
getTypeFromRangeTypeNode(node);
|
||||
}
|
||||
|
||||
function checkMappedType(node: MappedTypeNode) {
|
||||
checkSourceElement(node.typeParameter);
|
||||
checkSourceElement(node.type);
|
||||
@ -33671,6 +34343,10 @@ namespace ts {
|
||||
return checkSourceElement((node as JSDocTypeExpression).type);
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
return checkIndexedAccessType(<IndexedAccessTypeNode>node);
|
||||
case SyntaxKind.InverseOffsetType:
|
||||
return checkOffsetType(<InverseOffsetTypeNode>node);
|
||||
case SyntaxKind.RangeType:
|
||||
return checkRangeType(<RangeTypeNode>node);
|
||||
case SyntaxKind.MappedType:
|
||||
return checkMappedType(<MappedTypeNode>node);
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
|
||||
@ -1351,11 +1351,21 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
let lazyIsArray = (value: any): boolean => {
|
||||
if (typeof Array.isArray === "function") {
|
||||
lazyIsArray = Array.isArray;
|
||||
}
|
||||
else {
|
||||
lazyIsArray = value => value instanceof Array;
|
||||
}
|
||||
return lazyIsArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a value is an array.
|
||||
*/
|
||||
export function isArray(value: any): value is readonly {}[] {
|
||||
return Array.isArray ? Array.isArray(value) : value instanceof Array;
|
||||
return lazyIsArray(value);
|
||||
}
|
||||
|
||||
export function toArray<T>(value: T | T[]): T[];
|
||||
@ -1367,13 +1377,63 @@ namespace ts {
|
||||
/**
|
||||
* Tests whether a value is string
|
||||
*/
|
||||
export function isString(text: unknown): text is string {
|
||||
return typeof text === "string";
|
||||
export function isString(x: unknown): x is string {
|
||||
return typeof x === "string";
|
||||
}
|
||||
|
||||
export function isNumber(x: unknown): x is number {
|
||||
return typeof x === "number";
|
||||
}
|
||||
|
||||
let lazyIsInteger = (x: number): boolean => {
|
||||
if (typeof Number.isInteger === "function") {
|
||||
lazyIsInteger = Number.isInteger;
|
||||
}
|
||||
else {
|
||||
// https://tc39.es/ecma262/#sec-isinteger
|
||||
lazyIsInteger = x =>
|
||||
typeof x === "number" &&
|
||||
!isNaN(x) &&
|
||||
isFinite(x) &&
|
||||
Math.floor(Math.abs(x)) === Math.abs(x);
|
||||
}
|
||||
return lazyIsInteger(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the provided value is an integer.
|
||||
*
|
||||
* This emulates the behavior of [IsInteger (ECMA-262)](https://tc39.es/ecma262/#sec-isinteger).
|
||||
*/
|
||||
export function isInteger(x: number): boolean {
|
||||
return lazyIsInteger(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numeric value of a string, if that numeric value's string representation is equal
|
||||
* to the source string; otherwise, returns `undefined`.
|
||||
*
|
||||
* This determines whether a numeric string is a valid numeric index (e.g., the string representation
|
||||
* can be round-tripped through `ToString(ToNumber(x))`).
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* - `"1"` returns `1`, because:
|
||||
* - `ToNumber("1") === 1`
|
||||
* - `ToString(1) === "1"`
|
||||
* - `"1.0"` returns `undefined`, because:
|
||||
* - `ToNumber("1.0") === 1`
|
||||
* - `ToString(1) !== "1.0"`.
|
||||
*
|
||||
* This emulates the behavior of [CanonicalNumericIndexString (ECMA-262)](https://tc39.es/ecma262/#sec-canonicalnumericindexstring).
|
||||
*/
|
||||
export function canonicalNumericIndex(x: string | __String): number | undefined {
|
||||
if (x === "-0") return -0;
|
||||
const n = +x;
|
||||
if (x !== n + "") return undefined;
|
||||
return n;
|
||||
}
|
||||
|
||||
export function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined;
|
||||
export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined;
|
||||
export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined {
|
||||
|
||||
@ -10,7 +10,7 @@ namespace ts {
|
||||
return currentAssertionLevel >= level;
|
||||
}
|
||||
|
||||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): void {
|
||||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): asserts expression {
|
||||
if (!expression) {
|
||||
if (verboseDebugInfo) {
|
||||
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
|
||||
|
||||
@ -2879,6 +2879,10 @@
|
||||
"category": "Message",
|
||||
"code": 2782
|
||||
},
|
||||
"Type '{0}' cannot be used as an index type in an offset or range type.": {
|
||||
"category": "Error",
|
||||
"code": 2783
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
|
||||
@ -1332,6 +1332,8 @@ namespace ts {
|
||||
return emitConditionalType(<ConditionalTypeNode>node);
|
||||
case SyntaxKind.InferType:
|
||||
return emitInferType(<InferTypeNode>node);
|
||||
case SyntaxKind.InverseOffsetType:
|
||||
return emitOffsetType(<InverseOffsetTypeNode>node);
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
return emitParenthesizedType(<ParenthesizedTypeNode>node);
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
@ -1344,6 +1346,8 @@ namespace ts {
|
||||
return emitIndexedAccessType(<IndexedAccessTypeNode>node);
|
||||
case SyntaxKind.MappedType:
|
||||
return emitMappedType(<MappedTypeNode>node);
|
||||
case SyntaxKind.RangeType:
|
||||
return emitRangeType(<RangeTypeNode>node);
|
||||
case SyntaxKind.LiteralType:
|
||||
return emitLiteralType(<LiteralTypeNode>node);
|
||||
case SyntaxKind.ImportType:
|
||||
@ -2131,6 +2135,11 @@ namespace ts {
|
||||
emit(node.typeParameter);
|
||||
}
|
||||
|
||||
function emitOffsetType(node: InverseOffsetTypeNode) {
|
||||
writePunctuation("^");
|
||||
emit(node.indexType);
|
||||
}
|
||||
|
||||
function emitParenthesizedType(node: ParenthesizedTypeNode) {
|
||||
writePunctuation("(");
|
||||
emit(node.type);
|
||||
@ -2196,6 +2205,15 @@ namespace ts {
|
||||
writePunctuation("}");
|
||||
}
|
||||
|
||||
function emitRangeType(node: RangeTypeNode) {
|
||||
emit(node.objectType);
|
||||
writePunctuation("[");
|
||||
emit(node.startType);
|
||||
writePunctuation(":");
|
||||
emit(node.endType);
|
||||
writePunctuation("]");
|
||||
}
|
||||
|
||||
function emitLiteralType(node: LiteralTypeNode) {
|
||||
emitExpression(node.literal);
|
||||
}
|
||||
|
||||
@ -904,6 +904,18 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createInverseOffsetTypeNode(indexType: TypeNode) {
|
||||
const node = <InverseOffsetTypeNode>createSynthesizedNode(SyntaxKind.InverseOffsetType);
|
||||
node.indexType = parenthesizeElementTypeMember(indexType);
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateInverseOffsetTypeNode(node: InverseOffsetTypeNode, indexType: TypeNode) {
|
||||
return node.indexType !== indexType
|
||||
? updateNode(createInverseOffsetTypeNode(indexType), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createImportTypeNode(argument: TypeNode, qualifier?: EntityName, typeArguments?: readonly TypeNode[], isTypeOf?: boolean) {
|
||||
const node = <ImportTypeNode>createSynthesizedNode(SyntaxKind.ImportType);
|
||||
node.argument = argument;
|
||||
@ -958,7 +970,7 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode) {
|
||||
export function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode {
|
||||
return node.objectType !== objectType
|
||||
|| node.indexType !== indexType
|
||||
? updateNode(createIndexedAccessTypeNode(objectType, indexType), node)
|
||||
@ -983,6 +995,22 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createRangeTypeNode(objectType: TypeNode, startType: TypeNode | undefined, endType: TypeNode | undefined) {
|
||||
const node = createSynthesizedNode(SyntaxKind.RangeType) as RangeTypeNode;
|
||||
node.objectType = objectType;
|
||||
node.startType = startType;
|
||||
node.endType = endType;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateRangeTypeNode(node: RangeTypeNode, objectType: TypeNode, startType: TypeNode | undefined, endType: TypeNode | undefined) {
|
||||
return node.objectType !== objectType
|
||||
|| node.startType !== startType
|
||||
|| node.endType !== endType
|
||||
? updateNode(createRangeTypeNode(objectType, startType, endType), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createLiteralTypeNode(literal: LiteralTypeNode["literal"]) {
|
||||
const node = createSynthesizedNode(SyntaxKind.LiteralType) as LiteralTypeNode;
|
||||
node.literal = literal;
|
||||
|
||||
@ -190,6 +190,8 @@ namespace ts {
|
||||
visitNode(cbNode, (<ConditionalTypeNode>node).falseType);
|
||||
case SyntaxKind.InferType:
|
||||
return visitNode(cbNode, (<InferTypeNode>node).typeParameter);
|
||||
case SyntaxKind.InverseOffsetType:
|
||||
return visitNode(cbNode, (<InverseOffsetTypeNode>node).indexType);
|
||||
case SyntaxKind.ImportType:
|
||||
return visitNode(cbNode, (<ImportTypeNode>node).argument) ||
|
||||
visitNode(cbNode, (<ImportTypeNode>node).qualifier) ||
|
||||
@ -205,6 +207,10 @@ namespace ts {
|
||||
visitNode(cbNode, (<MappedTypeNode>node).typeParameter) ||
|
||||
visitNode(cbNode, (<MappedTypeNode>node).questionToken) ||
|
||||
visitNode(cbNode, (<MappedTypeNode>node).type);
|
||||
case SyntaxKind.RangeType:
|
||||
return visitNode(cbNode, (<RangeTypeNode>node).objectType) ||
|
||||
visitNode(cbNode, (<RangeTypeNode>node).startType) ||
|
||||
visitNode(cbNode, (<RangeTypeNode>node).endType);
|
||||
case SyntaxKind.LiteralType:
|
||||
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
|
||||
case SyntaxKind.ObjectBindingPattern:
|
||||
@ -3165,6 +3171,7 @@ namespace ts {
|
||||
case SyntaxKind.InferKeyword:
|
||||
case SyntaxKind.ImportKeyword:
|
||||
case SyntaxKind.AssertsKeyword:
|
||||
case SyntaxKind.CaretToken:
|
||||
return true;
|
||||
case SyntaxKind.FunctionKeyword:
|
||||
return !inStartOfParameter;
|
||||
@ -3200,10 +3207,19 @@ namespace ts {
|
||||
break;
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
parseExpected(SyntaxKind.OpenBracketToken);
|
||||
if (isStartOfType()) {
|
||||
const indexType = isStartOfType() ? parseType() : undefined;
|
||||
if (parseOptionalToken(SyntaxKind.ColonToken)) {
|
||||
const node = createNode(SyntaxKind.RangeType, type.pos) as RangeTypeNode;
|
||||
node.objectType = type;
|
||||
node.startType = indexType;
|
||||
node.endType = isStartOfType() ? parseType() : undefined;
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
type = finishNode(node);
|
||||
}
|
||||
else if (indexType) {
|
||||
const node = createNode(SyntaxKind.IndexedAccessType, type.pos) as IndexedAccessTypeNode;
|
||||
node.objectType = type;
|
||||
node.indexType = parseType();
|
||||
node.indexType = indexType;
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
type = finishNode(node);
|
||||
}
|
||||
@ -3245,6 +3261,13 @@ namespace ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseOffsetType(): InverseOffsetTypeNode {
|
||||
const node = <InverseOffsetTypeNode>createNode(SyntaxKind.InverseOffsetType);
|
||||
parseExpected(SyntaxKind.CaretToken);
|
||||
node.indexType = parseType();
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseTypeOperatorOrHigher(): TypeNode {
|
||||
const operator = token();
|
||||
switch (operator) {
|
||||
@ -3254,6 +3277,8 @@ namespace ts {
|
||||
return parseTypeOperator(operator);
|
||||
case SyntaxKind.InferKeyword:
|
||||
return parseInferType();
|
||||
case SyntaxKind.CaretToken:
|
||||
return parseOffsetType();
|
||||
}
|
||||
return parsePostfixTypeOrHigher();
|
||||
}
|
||||
|
||||
@ -322,11 +322,13 @@ namespace ts {
|
||||
IntersectionType,
|
||||
ConditionalType,
|
||||
InferType,
|
||||
InverseOffsetType,
|
||||
ParenthesizedType,
|
||||
ThisType,
|
||||
TypeOperator,
|
||||
IndexedAccessType,
|
||||
MappedType,
|
||||
RangeType,
|
||||
LiteralType,
|
||||
ImportType,
|
||||
// Binding patterns
|
||||
@ -1310,6 +1312,11 @@ namespace ts {
|
||||
typeParameter: TypeParameterDeclaration;
|
||||
}
|
||||
|
||||
export interface InverseOffsetTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.InverseOffsetType;
|
||||
indexType: TypeNode;
|
||||
}
|
||||
|
||||
export interface ParenthesizedTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.ParenthesizedType;
|
||||
type: TypeNode;
|
||||
@ -1340,6 +1347,13 @@ namespace ts {
|
||||
type?: TypeNode;
|
||||
}
|
||||
|
||||
export interface RangeTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.RangeType;
|
||||
objectType: TypeNode;
|
||||
startType: TypeNode | undefined;
|
||||
endType: TypeNode | undefined;
|
||||
}
|
||||
|
||||
export interface LiteralTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.LiteralType;
|
||||
literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression;
|
||||
@ -4153,6 +4167,7 @@ namespace ts {
|
||||
Function = "__function", // Unnamed function expression
|
||||
Computed = "__computed", // Computed property name declaration with dynamic name
|
||||
Resolving = "__resolving__", // Indicator symbol used to mark partially resolved type aliases
|
||||
Boundary = "__boundary", // Indicator symbol for the unspecified upper or lower bound in a range type (i.e., `T[:]`)
|
||||
ExportEquals = "export=", // Export assignment symbol
|
||||
Default = "default", // Default export symbol (technically not wholly internal, but included here for usability)
|
||||
This = "this",
|
||||
@ -4281,6 +4296,8 @@ namespace ts {
|
||||
Conditional = 1 << 24, // T extends U ? X : Y
|
||||
Substitution = 1 << 25, // Type parameter substitution
|
||||
NonPrimitive = 1 << 26, // intrinsic object type
|
||||
Range = 1 << 27, // T[X:Y]
|
||||
InverseOffset = 1 << 28, // ^T
|
||||
|
||||
/* @internal */
|
||||
AnyOrUnknown = Any | Unknown,
|
||||
@ -4299,7 +4316,7 @@ namespace ts {
|
||||
/* @internal */
|
||||
Primitive = String | Number | BigInt | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol,
|
||||
StringLike = String | StringLiteral,
|
||||
NumberLike = Number | NumberLiteral | Enum,
|
||||
NumberLike = Number | NumberLiteral | Enum | InverseOffset,
|
||||
BigIntLike = BigInt | BigIntLiteral,
|
||||
BooleanLike = Boolean | BooleanLiteral,
|
||||
EnumLike = Enum | EnumLiteral,
|
||||
@ -4309,15 +4326,15 @@ namespace ts {
|
||||
DisjointDomains = NonPrimitive | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbolLike | VoidLike | Null,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = Object | Union | Intersection,
|
||||
TypeVariable = TypeParameter | IndexedAccess,
|
||||
InstantiableNonPrimitive = TypeVariable | Conditional | Substitution,
|
||||
InstantiablePrimitive = Index,
|
||||
TypeVariable = TypeParameter | IndexedAccess | Range,
|
||||
InstantiableNonPrimitive = TypeVariable | Conditional | Substitution | Range,
|
||||
InstantiablePrimitive = Index | InverseOffset,
|
||||
Instantiable = InstantiableNonPrimitive | InstantiablePrimitive,
|
||||
StructuredOrInstantiable = StructuredType | Instantiable,
|
||||
/* @internal */
|
||||
ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection,
|
||||
/* @internal */
|
||||
Simplifiable = IndexedAccess | Conditional,
|
||||
Simplifiable = IndexedAccess | Range | Conditional,
|
||||
// 'Narrowable' types are types where narrowing actually narrows.
|
||||
// This *should* be every type other than null, undefined, void, and never
|
||||
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
|
||||
@ -4687,7 +4704,7 @@ namespace ts {
|
||||
simplifiedForWriting?: Type;
|
||||
}
|
||||
|
||||
export type TypeVariable = TypeParameter | IndexedAccessType;
|
||||
export type TypeVariable = TypeParameter | IndexedAccessType | RangeType;
|
||||
|
||||
// keyof T types (TypeFlags.Index)
|
||||
export interface IndexType extends InstantiableType {
|
||||
@ -4738,6 +4755,20 @@ namespace ts {
|
||||
substitute: Type; // Type to substitute for type parameter
|
||||
}
|
||||
|
||||
// ^T (TypeFlags.Offset)
|
||||
export interface InverseOffsetType extends InstantiableType {
|
||||
indexType: Type;
|
||||
}
|
||||
|
||||
// T[X:Y] (TypeFlags.Range)
|
||||
export interface RangeType extends InstantiableType {
|
||||
objectType: Type;
|
||||
startType: Type;
|
||||
endType: Type;
|
||||
simplifiedForReading?: Type;
|
||||
simplifiedForWriting?: Type;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const enum JsxReferenceKind {
|
||||
Component,
|
||||
|
||||
@ -1000,6 +1000,10 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.InferType;
|
||||
}
|
||||
|
||||
export function isOffsetTypeNode(node: Node): node is InverseOffsetTypeNode {
|
||||
return node.kind === SyntaxKind.InverseOffsetType;
|
||||
}
|
||||
|
||||
export function isParenthesizedTypeNode(node: Node): node is ParenthesizedTypeNode {
|
||||
return node.kind === SyntaxKind.ParenthesizedType;
|
||||
}
|
||||
|
||||
@ -398,6 +398,10 @@ namespace ts {
|
||||
return updateInferTypeNode(<InferTypeNode>node,
|
||||
visitNode((<InferTypeNode>node).typeParameter, visitor, isTypeParameterDeclaration));
|
||||
|
||||
case SyntaxKind.InverseOffsetType:
|
||||
return updateInverseOffsetTypeNode(<InverseOffsetTypeNode>node,
|
||||
visitNode((<InverseOffsetTypeNode>node).indexType, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.ImportType:
|
||||
return updateImportTypeNode(<ImportTypeNode>node,
|
||||
visitNode((<ImportTypeNode>node).argument, visitor, isTypeNode),
|
||||
@ -426,6 +430,12 @@ namespace ts {
|
||||
visitNode((<MappedTypeNode>node).questionToken, tokenVisitor, isToken),
|
||||
visitNode((<MappedTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.RangeType:
|
||||
return updateRangeTypeNode((<RangeTypeNode>node),
|
||||
visitNode((<RangeTypeNode>node).objectType, visitor, isTypeNode),
|
||||
visitNode((<RangeTypeNode>node).startType, visitor, isTypeNode),
|
||||
visitNode((<RangeTypeNode>node).endType, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.LiteralType:
|
||||
return updateLiteralTypeNode(<LiteralTypeNode>node,
|
||||
visitNode((<LiteralTypeNode>node).literal, visitor, isExpression));
|
||||
|
||||
@ -35,16 +35,17 @@ namespace ts.codefix {
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
let suggestedSymbol: Symbol | undefined;
|
||||
if (isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
||||
const { parent } = node;
|
||||
if (isPropertyAccessExpression(parent) && parent.name === node) {
|
||||
Debug.assert(isIdentifierOrPrivateIdentifier(node), "Expected an identifier for spelling (property access)");
|
||||
let containingType = checker.getTypeAtLocation(node.parent.expression);
|
||||
let containingType = checker.getTypeAtLocation(parent.expression);
|
||||
if (node.parent.flags & NodeFlags.OptionalChain) {
|
||||
containingType = checker.getNonNullableType(containingType);
|
||||
}
|
||||
const name = node as Identifier | PrivateIdentifier;
|
||||
suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(name, containingType);
|
||||
}
|
||||
else if (isImportSpecifier(node.parent) && node.parent.name === node) {
|
||||
else if (isImportSpecifier(parent) && parent.name === node) {
|
||||
Debug.assert(node.kind === SyntaxKind.Identifier, "Expected an identifier for spelling (import)");
|
||||
const importDeclaration = findAncestor(node, isImportDeclaration)!;
|
||||
const resolvedSourceFile = getResolvedSourceFileFromImportDeclaration(sourceFile, context, importDeclaration);
|
||||
|
||||
196
tests/baselines/reference/ranges.errors.txt
Normal file
196
tests/baselines/reference/ranges.errors.txt
Normal file
@ -0,0 +1,196 @@
|
||||
tests/cases/conformance/types/range/semantics.ts(14,15): error TS2461: Type '{}' is not an array type.
|
||||
tests/cases/conformance/types/range/semantics.ts(21,25): error TS2536: Type 'true' cannot be used to index type '[1, 2, 3]'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/range/semantics.ts (2 errors) ====
|
||||
// Rules (from https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c)
|
||||
|
||||
// Semantic Rules
|
||||
{
|
||||
// - The result of a *Range Type* has the same mutability as its *object type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = (readonly [1, 2, 3])[0:2];
|
||||
}
|
||||
|
||||
{
|
||||
// - The *object type* of a *Range Type* is constrained to be an *Array Type* or a *Tuple Type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = number[][0:2];
|
||||
type T3 = {}[0:2]; // error
|
||||
~~~~~~~
|
||||
!!! error TS2461: Type '{}' is not an array type.
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* and *end type* of a *Range Type* are constrained to `string | number`.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = [1, 2, 3]["0":"2"];
|
||||
type T3 = [1, 2, 3][true:false]; // error
|
||||
~~~~
|
||||
!!! error TS2536: Type 'true' cannot be used to index type '[1, 2, 3]'.
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* of a *Range Type* is optional. If not present, the *lower bound type* (`0`) is used.
|
||||
type T2 = [1, 2, 3][:1];
|
||||
|
||||
// - The *end type* of a *Range Type* is optional. If not present, the *upper bound type* (`^0`) is used.
|
||||
type T3 = [1, 2, 3][1:];
|
||||
type T1 = [1, 2, 3][:];
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *start type* or *end type* of a *Range Type* are negative-valued *Numeric Literal Types* (or numeric index-valued *String Literal Types*), they are treated
|
||||
// as an *Inverse Offset Type* for the absolute value of the numeric index value of the respective type.
|
||||
// - NOTE: This does not work for `-0` as JavaScript generally treats `-0` and `0` as the same value except for a few small corner cases and we must
|
||||
// align with this behavior.
|
||||
type T1 = [1, 2, 3, 4][1:-1];
|
||||
type T2 = [1, 2, 3, 4][1:-0];
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *object type* is a "generic object type", or if either of the *start type* or *end type* are
|
||||
// "generic index types", then the operation is deferred until it they can be instantiated.
|
||||
type T1<A extends any[]> = A[0:2];
|
||||
type T2 = T1<[1, 2, 3]>;
|
||||
type T3 = T1<number[]>;
|
||||
|
||||
type T4<X extends number> = [1, 2, 3][X:];
|
||||
type T5 = T4<1>;
|
||||
|
||||
type T6<Y extends number> = [1, 2, 3][:Y];
|
||||
type T7 = T6<^1>;
|
||||
}
|
||||
|
||||
{
|
||||
// - If either the *object type*, *start type*, or *end type* are *Union Types*, the result is distributed over
|
||||
// each constituent in the following manner:
|
||||
// - The *object type* is distributed over any *Inverse Offset Type* constituent of *start type* or *end type*.
|
||||
// This is necessary as an *Inverse Offset Type* can have a different outcome depending on the *object type*
|
||||
// it is resolved against.
|
||||
// - The *start type* and *end type* are distributed over each constituent of the *object type*.
|
||||
// - The *object type* is distributed over each constituent of the *start type* and *end type*.
|
||||
// - The results of the distribution are either an *Intersection Type* (if the *Range Type* was a "write"
|
||||
// location), or a *Union Type* (if the *Range Type* was a "read" location).
|
||||
type T1 = ([1, 2, 3] | [2, 3, 4])[0:2];
|
||||
type T2 = [1, 2, 3][0|1:2];
|
||||
type T3 = [1, 2, 3][0:1|2];
|
||||
type T4 = ([1, 2, 3] | [2, 3, 4])[0|1:2|3];
|
||||
type T5 = ([1, 2, 3] | [9, 8])[0:^1];
|
||||
}
|
||||
|
||||
{
|
||||
// - Otherwise (or for each constituent of the distribution),
|
||||
{
|
||||
// - If neither the *start type* nor the *end type* are *String Literal Types* or a *Numeric Literal Types*, then
|
||||
// - Return an *Array Type* for the union of each element of the *object type*: `T[any:any] -> T[number][]`.
|
||||
type T1 = [1, 2, 3][number:number];
|
||||
type T2 = [1, 2, 3][string:string];
|
||||
type T3 = [1, 2, 3][any:any];
|
||||
type T4 = [1, 2, 3][never:never];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *end type*, but with a *start type* of `0`: `T[any:Y] -> T[:Y][number][]`.
|
||||
type T1 = [1, 2, 3][number:2];
|
||||
type T2 = [1, 2, 3][string:2];
|
||||
type T3 = [1, 2, 3][any:2];
|
||||
type T4 = [1, 2, 3][never:2];
|
||||
}
|
||||
{
|
||||
// - If the *end type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *start type*, but with an *end type* of `^0`: `T[X:any] -> T[X:][number][]`.
|
||||
type T1 = [1, 2, 3][1:number];
|
||||
type T2 = [1, 2, 3][1:string];
|
||||
type T3 = [1, 2, 3][1:any];
|
||||
type T4 = [1, 2, 3][1:never];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *lower bound type* (`0`) and the *end type* is the *upper bound type* (`^0`),
|
||||
// then we are including all elements: return the *object type* of the *Range Type*.
|
||||
type T1 = [1, 2, 3][0:^0];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *upper bound type* (`^0`) or the *end type* is the *lower bound type* (`0`),
|
||||
// then we are including no elements: return the empty *Tuple Type* (`[]`).
|
||||
type T1 = [1, 2, 3][^0:^0];
|
||||
type T2 = [1, 2, 3][0:0];
|
||||
}
|
||||
{
|
||||
// - If the *object type* is an *Array Type*, then
|
||||
{
|
||||
// - If the signs of both the *start type* and *end type* agree, then return
|
||||
// a fixed-length *Tuple Type* with a minimum length of `0` for the difference between the *end type* and
|
||||
// the *start type* whose elements are the element type of the *object type*: `T[][0:1] -> [T?]`.
|
||||
type T1 = number[][0:2];
|
||||
type T2 = number[][^2:^0];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type* and the *end type* is not, we can create a tuple of
|
||||
// `min(abs(start), end)` optional elements.
|
||||
type T1 = number[][^2:4];
|
||||
}
|
||||
{
|
||||
// - Otherwise, we cannot derive a fixed length: return the *object type* of the *Range Type*.
|
||||
type T1 = number[][0:^2];
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise, the *object type* is a *Tuple Type*:
|
||||
{
|
||||
// - If the *object type* has a rest element, then
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type*, return an *Array Type* for the union of each
|
||||
// *optional type* and the *rest element type*, along with the set of `n` right-most required
|
||||
// elements of the *object type* where `n` is the absolute value of the *start type*.
|
||||
type T1 = [1, 2, 3, ...4[]][^1:];
|
||||
type T2 = [1, 2, 3, ...4[]][^2:];
|
||||
type T3 = [1, 2, 3, ...4[]][^3:];
|
||||
type T4 = [1, 2, 3?, ...4[]][^1:];
|
||||
type T5 = [1, 2, 3?, ...4[]][^2:];
|
||||
type T6 = [1, 2, 3?, ...4[]][^3:];
|
||||
}
|
||||
{
|
||||
// - If the *end type* is an *Inverse Offset Type*, return a *Tuple Type* of the elements of the
|
||||
// *object type* starting from the index at *start type*, but whose minimum length is reduced by the
|
||||
// absolute value of the *end type*.
|
||||
type T1 = [1, 2, 3, ...4[]][1:^1];
|
||||
type T2 = [1, 2, 3, ...4[]][1:^2];
|
||||
type T3 = [1, 2, 3, ...4[]][1:^3];
|
||||
type T4 = [1, 2, 3?, ...4[]][1:^1];
|
||||
type T5 = [1, 2, 3?, ...4[]][1:^2];
|
||||
type T6 = [1, 2, 3?, ...4[]][1:^3];
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise,
|
||||
{
|
||||
// - Clamp the *start type* and *end type* to values between `0` and the length of *object type*.
|
||||
type T1 = [1, 2, 3][^5:10];
|
||||
}
|
||||
{
|
||||
// - Return a *Tuple Type* for the elements of *object type* starting at *start type* and ending at
|
||||
// *end type*.
|
||||
type T1 = [1, 2, 3][1: 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
==== tests/cases/conformance/types/range/assignability.ts (0 errors) ====
|
||||
// - `S` is assignable to `T[X:Y]` if `S` is assignable to `C`, where `C` is the base constraint of `T[X:Y]` for writing.
|
||||
function f1<T extends [1, 2, 3, 4], U extends 1 | 2>(t: T[U:^1], s: [3] & [2, 3]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
// - `S[X:Y]` is assignable to `T[]` if `S[number]` is assignable to `T`.
|
||||
function f2<T, S extends [T, T, T]>(t: T[], s: S[0:2]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
// - `S[XS:YS]` is assignable to `T[XT:YT]` if `S` is assignable to `T`, `XS` is assignable to `XT`, and `YS` is assignable to `YT`.
|
||||
function f3<T extends [1 | 9, 2 | 8, 3 | 7, 4 | 6], S extends T>(t: T[1 | 2:3], s: S[1:3]) {
|
||||
t = s;
|
||||
}
|
||||
262
tests/baselines/reference/ranges.js
Normal file
262
tests/baselines/reference/ranges.js
Normal file
@ -0,0 +1,262 @@
|
||||
//// [tests/cases/conformance/types/range/ranges.ts] ////
|
||||
|
||||
//// [semantics.ts]
|
||||
// Rules (from https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c)
|
||||
|
||||
// Semantic Rules
|
||||
{
|
||||
// - The result of a *Range Type* has the same mutability as its *object type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = (readonly [1, 2, 3])[0:2];
|
||||
}
|
||||
|
||||
{
|
||||
// - The *object type* of a *Range Type* is constrained to be an *Array Type* or a *Tuple Type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = number[][0:2];
|
||||
type T3 = {}[0:2]; // error
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* and *end type* of a *Range Type* are constrained to `string | number`.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = [1, 2, 3]["0":"2"];
|
||||
type T3 = [1, 2, 3][true:false]; // error
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* of a *Range Type* is optional. If not present, the *lower bound type* (`0`) is used.
|
||||
type T2 = [1, 2, 3][:1];
|
||||
|
||||
// - The *end type* of a *Range Type* is optional. If not present, the *upper bound type* (`^0`) is used.
|
||||
type T3 = [1, 2, 3][1:];
|
||||
type T1 = [1, 2, 3][:];
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *start type* or *end type* of a *Range Type* are negative-valued *Numeric Literal Types* (or numeric index-valued *String Literal Types*), they are treated
|
||||
// as an *Inverse Offset Type* for the absolute value of the numeric index value of the respective type.
|
||||
// - NOTE: This does not work for `-0` as JavaScript generally treats `-0` and `0` as the same value except for a few small corner cases and we must
|
||||
// align with this behavior.
|
||||
type T1 = [1, 2, 3, 4][1:-1];
|
||||
type T2 = [1, 2, 3, 4][1:-0];
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *object type* is a "generic object type", or if either of the *start type* or *end type* are
|
||||
// "generic index types", then the operation is deferred until it they can be instantiated.
|
||||
type T1<A extends any[]> = A[0:2];
|
||||
type T2 = T1<[1, 2, 3]>;
|
||||
type T3 = T1<number[]>;
|
||||
|
||||
type T4<X extends number> = [1, 2, 3][X:];
|
||||
type T5 = T4<1>;
|
||||
|
||||
type T6<Y extends number> = [1, 2, 3][:Y];
|
||||
type T7 = T6<^1>;
|
||||
}
|
||||
|
||||
{
|
||||
// - If either the *object type*, *start type*, or *end type* are *Union Types*, the result is distributed over
|
||||
// each constituent in the following manner:
|
||||
// - The *object type* is distributed over any *Inverse Offset Type* constituent of *start type* or *end type*.
|
||||
// This is necessary as an *Inverse Offset Type* can have a different outcome depending on the *object type*
|
||||
// it is resolved against.
|
||||
// - The *start type* and *end type* are distributed over each constituent of the *object type*.
|
||||
// - The *object type* is distributed over each constituent of the *start type* and *end type*.
|
||||
// - The results of the distribution are either an *Intersection Type* (if the *Range Type* was a "write"
|
||||
// location), or a *Union Type* (if the *Range Type* was a "read" location).
|
||||
type T1 = ([1, 2, 3] | [2, 3, 4])[0:2];
|
||||
type T2 = [1, 2, 3][0|1:2];
|
||||
type T3 = [1, 2, 3][0:1|2];
|
||||
type T4 = ([1, 2, 3] | [2, 3, 4])[0|1:2|3];
|
||||
type T5 = ([1, 2, 3] | [9, 8])[0:^1];
|
||||
}
|
||||
|
||||
{
|
||||
// - Otherwise (or for each constituent of the distribution),
|
||||
{
|
||||
// - If neither the *start type* nor the *end type* are *String Literal Types* or a *Numeric Literal Types*, then
|
||||
// - Return an *Array Type* for the union of each element of the *object type*: `T[any:any] -> T[number][]`.
|
||||
type T1 = [1, 2, 3][number:number];
|
||||
type T2 = [1, 2, 3][string:string];
|
||||
type T3 = [1, 2, 3][any:any];
|
||||
type T4 = [1, 2, 3][never:never];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *end type*, but with a *start type* of `0`: `T[any:Y] -> T[:Y][number][]`.
|
||||
type T1 = [1, 2, 3][number:2];
|
||||
type T2 = [1, 2, 3][string:2];
|
||||
type T3 = [1, 2, 3][any:2];
|
||||
type T4 = [1, 2, 3][never:2];
|
||||
}
|
||||
{
|
||||
// - If the *end type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *start type*, but with an *end type* of `^0`: `T[X:any] -> T[X:][number][]`.
|
||||
type T1 = [1, 2, 3][1:number];
|
||||
type T2 = [1, 2, 3][1:string];
|
||||
type T3 = [1, 2, 3][1:any];
|
||||
type T4 = [1, 2, 3][1:never];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *lower bound type* (`0`) and the *end type* is the *upper bound type* (`^0`),
|
||||
// then we are including all elements: return the *object type* of the *Range Type*.
|
||||
type T1 = [1, 2, 3][0:^0];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *upper bound type* (`^0`) or the *end type* is the *lower bound type* (`0`),
|
||||
// then we are including no elements: return the empty *Tuple Type* (`[]`).
|
||||
type T1 = [1, 2, 3][^0:^0];
|
||||
type T2 = [1, 2, 3][0:0];
|
||||
}
|
||||
{
|
||||
// - If the *object type* is an *Array Type*, then
|
||||
{
|
||||
// - If the signs of both the *start type* and *end type* agree, then return
|
||||
// a fixed-length *Tuple Type* with a minimum length of `0` for the difference between the *end type* and
|
||||
// the *start type* whose elements are the element type of the *object type*: `T[][0:1] -> [T?]`.
|
||||
type T1 = number[][0:2];
|
||||
type T2 = number[][^2:^0];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type* and the *end type* is not, we can create a tuple of
|
||||
// `min(abs(start), end)` optional elements.
|
||||
type T1 = number[][^2:4];
|
||||
}
|
||||
{
|
||||
// - Otherwise, we cannot derive a fixed length: return the *object type* of the *Range Type*.
|
||||
type T1 = number[][0:^2];
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise, the *object type* is a *Tuple Type*:
|
||||
{
|
||||
// - If the *object type* has a rest element, then
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type*, return an *Array Type* for the union of each
|
||||
// *optional type* and the *rest element type*, along with the set of `n` right-most required
|
||||
// elements of the *object type* where `n` is the absolute value of the *start type*.
|
||||
type T1 = [1, 2, 3, ...4[]][^1:];
|
||||
type T2 = [1, 2, 3, ...4[]][^2:];
|
||||
type T3 = [1, 2, 3, ...4[]][^3:];
|
||||
type T4 = [1, 2, 3?, ...4[]][^1:];
|
||||
type T5 = [1, 2, 3?, ...4[]][^2:];
|
||||
type T6 = [1, 2, 3?, ...4[]][^3:];
|
||||
}
|
||||
{
|
||||
// - If the *end type* is an *Inverse Offset Type*, return a *Tuple Type* of the elements of the
|
||||
// *object type* starting from the index at *start type*, but whose minimum length is reduced by the
|
||||
// absolute value of the *end type*.
|
||||
type T1 = [1, 2, 3, ...4[]][1:^1];
|
||||
type T2 = [1, 2, 3, ...4[]][1:^2];
|
||||
type T3 = [1, 2, 3, ...4[]][1:^3];
|
||||
type T4 = [1, 2, 3?, ...4[]][1:^1];
|
||||
type T5 = [1, 2, 3?, ...4[]][1:^2];
|
||||
type T6 = [1, 2, 3?, ...4[]][1:^3];
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise,
|
||||
{
|
||||
// - Clamp the *start type* and *end type* to values between `0` and the length of *object type*.
|
||||
type T1 = [1, 2, 3][^5:10];
|
||||
}
|
||||
{
|
||||
// - Return a *Tuple Type* for the elements of *object type* starting at *start type* and ending at
|
||||
// *end type*.
|
||||
type T1 = [1, 2, 3][1: 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// [assignability.ts]
|
||||
// - `S` is assignable to `T[X:Y]` if `S` is assignable to `C`, where `C` is the base constraint of `T[X:Y]` for writing.
|
||||
function f1<T extends [1, 2, 3, 4], U extends 1 | 2>(t: T[U:^1], s: [3] & [2, 3]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
// - `S[X:Y]` is assignable to `T[]` if `S[number]` is assignable to `T`.
|
||||
function f2<T, S extends [T, T, T]>(t: T[], s: S[0:2]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
// - `S[XS:YS]` is assignable to `T[XT:YT]` if `S` is assignable to `T`, `XS` is assignable to `XT`, and `YS` is assignable to `YT`.
|
||||
function f3<T extends [1 | 9, 2 | 8, 3 | 7, 4 | 6], S extends T>(t: T[1 | 2:3], s: S[1:3]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
//// [semantics.js]
|
||||
"use strict";
|
||||
// Rules (from https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c)
|
||||
// Semantic Rules
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
// - Otherwise (or for each constituent of the distribution),
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
// - If the *object type* is an *Array Type*, then
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise, the *object type* is a *Tuple Type*:
|
||||
{
|
||||
// - If the *object type* has a rest element, then
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise,
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//// [assignability.js]
|
||||
"use strict";
|
||||
// - `S` is assignable to `T[X:Y]` if `S` is assignable to `C`, where `C` is the base constraint of `T[X:Y]` for writing.
|
||||
function f1(t, s) {
|
||||
t = s;
|
||||
}
|
||||
// - `S[X:Y]` is assignable to `T[]` if `S[number]` is assignable to `T`.
|
||||
function f2(t, s) {
|
||||
t = s;
|
||||
}
|
||||
// - `S[XS:YS]` is assignable to `T[XT:YT]` if `S` is assignable to `T`, `XS` is assignable to `XT`, and `YS` is assignable to `YT`.
|
||||
function f3(t, s) {
|
||||
t = s;
|
||||
}
|
||||
326
tests/baselines/reference/ranges.symbols
Normal file
326
tests/baselines/reference/ranges.symbols
Normal file
@ -0,0 +1,326 @@
|
||||
=== tests/cases/conformance/types/range/semantics.ts ===
|
||||
// Rules (from https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c)
|
||||
|
||||
// Semantic Rules
|
||||
{
|
||||
// - The result of a *Range Type* has the same mutability as its *object type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 3, 1))
|
||||
|
||||
type T2 = (readonly [1, 2, 3])[0:2];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 5, 29))
|
||||
}
|
||||
|
||||
{
|
||||
// - The *object type* of a *Range Type* is constrained to be an *Array Type* or a *Tuple Type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 9, 1))
|
||||
|
||||
type T2 = number[][0:2];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 11, 29))
|
||||
|
||||
type T3 = {}[0:2]; // error
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 12, 28))
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* and *end type* of a *Range Type* are constrained to `string | number`.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 16, 1))
|
||||
|
||||
type T2 = [1, 2, 3]["0":"2"];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 18, 29))
|
||||
|
||||
type T3 = [1, 2, 3][true:false]; // error
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 19, 33))
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* of a *Range Type* is optional. If not present, the *lower bound type* (`0`) is used.
|
||||
type T2 = [1, 2, 3][:1];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 23, 1))
|
||||
|
||||
// - The *end type* of a *Range Type* is optional. If not present, the *upper bound type* (`^0`) is used.
|
||||
type T3 = [1, 2, 3][1:];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 25, 28))
|
||||
|
||||
type T1 = [1, 2, 3][:];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 28, 28))
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *start type* or *end type* of a *Range Type* are negative-valued *Numeric Literal Types* (or numeric index-valued *String Literal Types*), they are treated
|
||||
// as an *Inverse Offset Type* for the absolute value of the numeric index value of the respective type.
|
||||
// - NOTE: This does not work for `-0` as JavaScript generally treats `-0` and `0` as the same value except for a few small corner cases and we must
|
||||
// align with this behavior.
|
||||
type T1 = [1, 2, 3, 4][1:-1];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 32, 1))
|
||||
|
||||
type T2 = [1, 2, 3, 4][1:-0];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 37, 33))
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *object type* is a "generic object type", or if either of the *start type* or *end type* are
|
||||
// "generic index types", then the operation is deferred until it they can be instantiated.
|
||||
type T1<A extends any[]> = A[0:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 41, 1))
|
||||
>A : Symbol(A, Decl(semantics.ts, 44, 12))
|
||||
>A : Symbol(A, Decl(semantics.ts, 44, 12))
|
||||
|
||||
type T2 = T1<[1, 2, 3]>;
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 44, 38))
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 41, 1))
|
||||
|
||||
type T3 = T1<number[]>;
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 45, 28))
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 41, 1))
|
||||
|
||||
type T4<X extends number> = [1, 2, 3][X:];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 46, 27))
|
||||
>X : Symbol(X, Decl(semantics.ts, 48, 12))
|
||||
>X : Symbol(X, Decl(semantics.ts, 48, 12))
|
||||
|
||||
type T5 = T4<1>;
|
||||
>T5 : Symbol(T5, Decl(semantics.ts, 48, 46))
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 46, 27))
|
||||
|
||||
type T6<Y extends number> = [1, 2, 3][:Y];
|
||||
>T6 : Symbol(T6, Decl(semantics.ts, 49, 20))
|
||||
>Y : Symbol(Y, Decl(semantics.ts, 51, 12))
|
||||
>Y : Symbol(Y, Decl(semantics.ts, 51, 12))
|
||||
|
||||
type T7 = T6<^1>;
|
||||
>T7 : Symbol(T7, Decl(semantics.ts, 51, 46))
|
||||
>T6 : Symbol(T6, Decl(semantics.ts, 49, 20))
|
||||
}
|
||||
|
||||
{
|
||||
// - If either the *object type*, *start type*, or *end type* are *Union Types*, the result is distributed over
|
||||
// each constituent in the following manner:
|
||||
// - The *object type* is distributed over any *Inverse Offset Type* constituent of *start type* or *end type*.
|
||||
// This is necessary as an *Inverse Offset Type* can have a different outcome depending on the *object type*
|
||||
// it is resolved against.
|
||||
// - The *start type* and *end type* are distributed over each constituent of the *object type*.
|
||||
// - The *object type* is distributed over each constituent of the *start type* and *end type*.
|
||||
// - The results of the distribution are either an *Intersection Type* (if the *Range Type* was a "write"
|
||||
// location), or a *Union Type* (if the *Range Type* was a "read" location).
|
||||
type T1 = ([1, 2, 3] | [2, 3, 4])[0:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 55, 1))
|
||||
|
||||
type T2 = [1, 2, 3][0|1:2];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 65, 43))
|
||||
|
||||
type T3 = [1, 2, 3][0:1|2];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 66, 31))
|
||||
|
||||
type T4 = ([1, 2, 3] | [2, 3, 4])[0|1:2|3];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 67, 31))
|
||||
|
||||
type T5 = ([1, 2, 3] | [9, 8])[0:^1];
|
||||
>T5 : Symbol(T5, Decl(semantics.ts, 68, 47))
|
||||
}
|
||||
|
||||
{
|
||||
// - Otherwise (or for each constituent of the distribution),
|
||||
{
|
||||
// - If neither the *start type* nor the *end type* are *String Literal Types* or a *Numeric Literal Types*, then
|
||||
// - Return an *Array Type* for the union of each element of the *object type*: `T[any:any] -> T[number][]`.
|
||||
type T1 = [1, 2, 3][number:number];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 74, 5))
|
||||
|
||||
type T2 = [1, 2, 3][string:string];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 77, 43))
|
||||
|
||||
type T3 = [1, 2, 3][any:any];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 78, 43))
|
||||
|
||||
type T4 = [1, 2, 3][never:never];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 79, 37))
|
||||
}
|
||||
{
|
||||
// - If the *start type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *end type*, but with a *start type* of `0`: `T[any:Y] -> T[:Y][number][]`.
|
||||
type T1 = [1, 2, 3][number:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 82, 5))
|
||||
|
||||
type T2 = [1, 2, 3][string:2];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 86, 38))
|
||||
|
||||
type T3 = [1, 2, 3][any:2];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 87, 38))
|
||||
|
||||
type T4 = [1, 2, 3][never:2];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 88, 35))
|
||||
}
|
||||
{
|
||||
// - If the *end type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *start type*, but with an *end type* of `^0`: `T[X:any] -> T[X:][number][]`.
|
||||
type T1 = [1, 2, 3][1:number];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 91, 5))
|
||||
|
||||
type T2 = [1, 2, 3][1:string];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 95, 38))
|
||||
|
||||
type T3 = [1, 2, 3][1:any];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 96, 38))
|
||||
|
||||
type T4 = [1, 2, 3][1:never];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 97, 35))
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *lower bound type* (`0`) and the *end type* is the *upper bound type* (`^0`),
|
||||
// then we are including all elements: return the *object type* of the *Range Type*.
|
||||
type T1 = [1, 2, 3][0:^0];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 100, 5))
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *upper bound type* (`^0`) or the *end type* is the *lower bound type* (`0`),
|
||||
// then we are including no elements: return the empty *Tuple Type* (`[]`).
|
||||
type T1 = [1, 2, 3][^0:^0];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 105, 5))
|
||||
|
||||
type T2 = [1, 2, 3][0:0];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 108, 35))
|
||||
}
|
||||
{
|
||||
// - If the *object type* is an *Array Type*, then
|
||||
{
|
||||
// - If the signs of both the *start type* and *end type* agree, then return
|
||||
// a fixed-length *Tuple Type* with a minimum length of `0` for the difference between the *end type* and
|
||||
// the *start type* whose elements are the element type of the *object type*: `T[][0:1] -> [T?]`.
|
||||
type T1 = number[][0:2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 113, 9))
|
||||
|
||||
type T2 = number[][^2:^0];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 117, 36))
|
||||
}
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type* and the *end type* is not, we can create a tuple of
|
||||
// `min(abs(start), end)` optional elements.
|
||||
type T1 = number[][^2:4];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 120, 9))
|
||||
}
|
||||
{
|
||||
// - Otherwise, we cannot derive a fixed length: return the *object type* of the *Range Type*.
|
||||
type T1 = number[][0:^2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 125, 9))
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise, the *object type* is a *Tuple Type*:
|
||||
{
|
||||
// - If the *object type* has a rest element, then
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type*, return an *Array Type* for the union of each
|
||||
// *optional type* and the *rest element type*, along with the set of `n` right-most required
|
||||
// elements of the *object type* where `n` is the absolute value of the *start type*.
|
||||
type T1 = [1, 2, 3, ...4[]][^1:];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 134, 13))
|
||||
|
||||
type T2 = [1, 2, 3, ...4[]][^2:];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 138, 49))
|
||||
|
||||
type T3 = [1, 2, 3, ...4[]][^3:];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 139, 49))
|
||||
|
||||
type T4 = [1, 2, 3?, ...4[]][^1:];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 140, 49))
|
||||
|
||||
type T5 = [1, 2, 3?, ...4[]][^2:];
|
||||
>T5 : Symbol(T5, Decl(semantics.ts, 141, 50))
|
||||
|
||||
type T6 = [1, 2, 3?, ...4[]][^3:];
|
||||
>T6 : Symbol(T6, Decl(semantics.ts, 142, 50))
|
||||
}
|
||||
{
|
||||
// - If the *end type* is an *Inverse Offset Type*, return a *Tuple Type* of the elements of the
|
||||
// *object type* starting from the index at *start type*, but whose minimum length is reduced by the
|
||||
// absolute value of the *end type*.
|
||||
type T1 = [1, 2, 3, ...4[]][1:^1];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 145, 13))
|
||||
|
||||
type T2 = [1, 2, 3, ...4[]][1:^2];
|
||||
>T2 : Symbol(T2, Decl(semantics.ts, 149, 50))
|
||||
|
||||
type T3 = [1, 2, 3, ...4[]][1:^3];
|
||||
>T3 : Symbol(T3, Decl(semantics.ts, 150, 50))
|
||||
|
||||
type T4 = [1, 2, 3?, ...4[]][1:^1];
|
||||
>T4 : Symbol(T4, Decl(semantics.ts, 151, 50))
|
||||
|
||||
type T5 = [1, 2, 3?, ...4[]][1:^2];
|
||||
>T5 : Symbol(T5, Decl(semantics.ts, 152, 51))
|
||||
|
||||
type T6 = [1, 2, 3?, ...4[]][1:^3];
|
||||
>T6 : Symbol(T6, Decl(semantics.ts, 153, 51))
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise,
|
||||
{
|
||||
// - Clamp the *start type* and *end type* to values between `0` and the length of *object type*.
|
||||
type T1 = [1, 2, 3][^5:10];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 159, 13))
|
||||
}
|
||||
{
|
||||
// - Return a *Tuple Type* for the elements of *object type* starting at *start type* and ending at
|
||||
// *end type*.
|
||||
type T1 = [1, 2, 3][1: 2];
|
||||
>T1 : Symbol(T1, Decl(semantics.ts, 163, 13))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/types/range/assignability.ts ===
|
||||
// - `S` is assignable to `T[X:Y]` if `S` is assignable to `C`, where `C` is the base constraint of `T[X:Y]` for writing.
|
||||
function f1<T extends [1, 2, 3, 4], U extends 1 | 2>(t: T[U:^1], s: [3] & [2, 3]) {
|
||||
>f1 : Symbol(f1, Decl(assignability.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(assignability.ts, 1, 12))
|
||||
>U : Symbol(U, Decl(assignability.ts, 1, 35))
|
||||
>t : Symbol(t, Decl(assignability.ts, 1, 53))
|
||||
>T : Symbol(T, Decl(assignability.ts, 1, 12))
|
||||
>U : Symbol(U, Decl(assignability.ts, 1, 35))
|
||||
>s : Symbol(s, Decl(assignability.ts, 1, 64))
|
||||
|
||||
t = s;
|
||||
>t : Symbol(t, Decl(assignability.ts, 1, 53))
|
||||
>s : Symbol(s, Decl(assignability.ts, 1, 64))
|
||||
}
|
||||
|
||||
// - `S[X:Y]` is assignable to `T[]` if `S[number]` is assignable to `T`.
|
||||
function f2<T, S extends [T, T, T]>(t: T[], s: S[0:2]) {
|
||||
>f2 : Symbol(f2, Decl(assignability.ts, 3, 1))
|
||||
>T : Symbol(T, Decl(assignability.ts, 6, 12))
|
||||
>S : Symbol(S, Decl(assignability.ts, 6, 14))
|
||||
>T : Symbol(T, Decl(assignability.ts, 6, 12))
|
||||
>T : Symbol(T, Decl(assignability.ts, 6, 12))
|
||||
>T : Symbol(T, Decl(assignability.ts, 6, 12))
|
||||
>t : Symbol(t, Decl(assignability.ts, 6, 36))
|
||||
>T : Symbol(T, Decl(assignability.ts, 6, 12))
|
||||
>s : Symbol(s, Decl(assignability.ts, 6, 43))
|
||||
>S : Symbol(S, Decl(assignability.ts, 6, 14))
|
||||
|
||||
t = s;
|
||||
>t : Symbol(t, Decl(assignability.ts, 6, 36))
|
||||
>s : Symbol(s, Decl(assignability.ts, 6, 43))
|
||||
}
|
||||
|
||||
// - `S[XS:YS]` is assignable to `T[XT:YT]` if `S` is assignable to `T`, `XS` is assignable to `XT`, and `YS` is assignable to `YT`.
|
||||
function f3<T extends [1 | 9, 2 | 8, 3 | 7, 4 | 6], S extends T>(t: T[1 | 2:3], s: S[1:3]) {
|
||||
>f3 : Symbol(f3, Decl(assignability.ts, 8, 1))
|
||||
>T : Symbol(T, Decl(assignability.ts, 11, 12))
|
||||
>S : Symbol(S, Decl(assignability.ts, 11, 51))
|
||||
>T : Symbol(T, Decl(assignability.ts, 11, 12))
|
||||
>t : Symbol(t, Decl(assignability.ts, 11, 65))
|
||||
>T : Symbol(T, Decl(assignability.ts, 11, 12))
|
||||
>s : Symbol(s, Decl(assignability.ts, 11, 79))
|
||||
>S : Symbol(S, Decl(assignability.ts, 11, 51))
|
||||
|
||||
t = s;
|
||||
>t : Symbol(t, Decl(assignability.ts, 11, 65))
|
||||
>s : Symbol(s, Decl(assignability.ts, 11, 79))
|
||||
}
|
||||
309
tests/baselines/reference/ranges.types
Normal file
309
tests/baselines/reference/ranges.types
Normal file
@ -0,0 +1,309 @@
|
||||
=== tests/cases/conformance/types/range/semantics.ts ===
|
||||
// Rules (from https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c)
|
||||
|
||||
// Semantic Rules
|
||||
{
|
||||
// - The result of a *Range Type* has the same mutability as its *object type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
>T1 : [1, 2]
|
||||
|
||||
type T2 = (readonly [1, 2, 3])[0:2];
|
||||
>T2 : readonly [1, 2]
|
||||
}
|
||||
|
||||
{
|
||||
// - The *object type* of a *Range Type* is constrained to be an *Array Type* or a *Tuple Type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
>T1 : [1, 2]
|
||||
|
||||
type T2 = number[][0:2];
|
||||
>T2 : [number?, number?]
|
||||
|
||||
type T3 = {}[0:2]; // error
|
||||
>T3 : any
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* and *end type* of a *Range Type* are constrained to `string | number`.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
>T1 : [1, 2]
|
||||
|
||||
type T2 = [1, 2, 3]["0":"2"];
|
||||
>T2 : [1, 2]
|
||||
|
||||
type T3 = [1, 2, 3][true:false]; // error
|
||||
>T3 : any
|
||||
>true : true
|
||||
>false : false
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* of a *Range Type* is optional. If not present, the *lower bound type* (`0`) is used.
|
||||
type T2 = [1, 2, 3][:1];
|
||||
>T2 : [1]
|
||||
|
||||
// - The *end type* of a *Range Type* is optional. If not present, the *upper bound type* (`^0`) is used.
|
||||
type T3 = [1, 2, 3][1:];
|
||||
>T3 : [2, 3]
|
||||
|
||||
type T1 = [1, 2, 3][:];
|
||||
>T1 : [1, 2, 3]
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *start type* or *end type* of a *Range Type* are negative-valued *Numeric Literal Types* (or numeric index-valued *String Literal Types*), they are treated
|
||||
// as an *Inverse Offset Type* for the absolute value of the numeric index value of the respective type.
|
||||
// - NOTE: This does not work for `-0` as JavaScript generally treats `-0` and `0` as the same value except for a few small corner cases and we must
|
||||
// align with this behavior.
|
||||
type T1 = [1, 2, 3, 4][1:-1];
|
||||
>T1 : [2, 3]
|
||||
>-1 : -1
|
||||
>1 : 1
|
||||
|
||||
type T2 = [1, 2, 3, 4][1:-0];
|
||||
>T2 : []
|
||||
>-0 : 0
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *object type* is a "generic object type", or if either of the *start type* or *end type* are
|
||||
// "generic index types", then the operation is deferred until it they can be instantiated.
|
||||
type T1<A extends any[]> = A[0:2];
|
||||
>T1 : A[0:2]
|
||||
|
||||
type T2 = T1<[1, 2, 3]>;
|
||||
>T2 : [1, 2]
|
||||
|
||||
type T3 = T1<number[]>;
|
||||
>T3 : [number?, number?]
|
||||
|
||||
type T4<X extends number> = [1, 2, 3][X:];
|
||||
>T4 : [1, 2, 3][X:]
|
||||
|
||||
type T5 = T4<1>;
|
||||
>T5 : [2, 3]
|
||||
|
||||
type T6<Y extends number> = [1, 2, 3][:Y];
|
||||
>T6 : [1, 2, 3][:Y]
|
||||
|
||||
type T7 = T6<^1>;
|
||||
>T7 : [1, 2]
|
||||
}
|
||||
|
||||
{
|
||||
// - If either the *object type*, *start type*, or *end type* are *Union Types*, the result is distributed over
|
||||
// each constituent in the following manner:
|
||||
// - The *object type* is distributed over any *Inverse Offset Type* constituent of *start type* or *end type*.
|
||||
// This is necessary as an *Inverse Offset Type* can have a different outcome depending on the *object type*
|
||||
// it is resolved against.
|
||||
// - The *start type* and *end type* are distributed over each constituent of the *object type*.
|
||||
// - The *object type* is distributed over each constituent of the *start type* and *end type*.
|
||||
// - The results of the distribution are either an *Intersection Type* (if the *Range Type* was a "write"
|
||||
// location), or a *Union Type* (if the *Range Type* was a "read" location).
|
||||
type T1 = ([1, 2, 3] | [2, 3, 4])[0:2];
|
||||
>T1 : [1, 2] | [2, 3]
|
||||
|
||||
type T2 = [1, 2, 3][0|1:2];
|
||||
>T2 : [1, 2] | [2]
|
||||
|
||||
type T3 = [1, 2, 3][0:1|2];
|
||||
>T3 : [1, 2] | [1]
|
||||
|
||||
type T4 = ([1, 2, 3] | [2, 3, 4])[0|1:2|3];
|
||||
>T4 : [1, 2, 3] | [1, 2] | [2, 3] | [2, 3, 4] | [2] | [3, 4] | [3]
|
||||
|
||||
type T5 = ([1, 2, 3] | [9, 8])[0:^1];
|
||||
>T5 : [1, 2] | [9]
|
||||
}
|
||||
|
||||
{
|
||||
// - Otherwise (or for each constituent of the distribution),
|
||||
{
|
||||
// - If neither the *start type* nor the *end type* are *String Literal Types* or a *Numeric Literal Types*, then
|
||||
// - Return an *Array Type* for the union of each element of the *object type*: `T[any:any] -> T[number][]`.
|
||||
type T1 = [1, 2, 3][number:number];
|
||||
>T1 : (3 | 1 | 2)[]
|
||||
|
||||
type T2 = [1, 2, 3][string:string];
|
||||
>T2 : (3 | 1 | 2)[]
|
||||
|
||||
type T3 = [1, 2, 3][any:any];
|
||||
>T3 : (3 | 1 | 2)[]
|
||||
|
||||
type T4 = [1, 2, 3][never:never];
|
||||
>T4 : (3 | 1 | 2)[]
|
||||
}
|
||||
{
|
||||
// - If the *start type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *end type*, but with a *start type* of `0`: `T[any:Y] -> T[:Y][number][]`.
|
||||
type T1 = [1, 2, 3][number:2];
|
||||
>T1 : (1 | 2)[]
|
||||
|
||||
type T2 = [1, 2, 3][string:2];
|
||||
>T2 : (1 | 2)[]
|
||||
|
||||
type T3 = [1, 2, 3][any:2];
|
||||
>T3 : (1 | 2)[]
|
||||
|
||||
type T4 = [1, 2, 3][never:2];
|
||||
>T4 : (1 | 2)[]
|
||||
}
|
||||
{
|
||||
// - If the *end type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *start type*, but with an *end type* of `^0`: `T[X:any] -> T[X:][number][]`.
|
||||
type T1 = [1, 2, 3][1:number];
|
||||
>T1 : (3 | 2)[]
|
||||
|
||||
type T2 = [1, 2, 3][1:string];
|
||||
>T2 : (3 | 2)[]
|
||||
|
||||
type T3 = [1, 2, 3][1:any];
|
||||
>T3 : (3 | 2)[]
|
||||
|
||||
type T4 = [1, 2, 3][1:never];
|
||||
>T4 : (3 | 2)[]
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *lower bound type* (`0`) and the *end type* is the *upper bound type* (`^0`),
|
||||
// then we are including all elements: return the *object type* of the *Range Type*.
|
||||
type T1 = [1, 2, 3][0:^0];
|
||||
>T1 : [1, 2, 3]
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *upper bound type* (`^0`) or the *end type* is the *lower bound type* (`0`),
|
||||
// then we are including no elements: return the empty *Tuple Type* (`[]`).
|
||||
type T1 = [1, 2, 3][^0:^0];
|
||||
>T1 : []
|
||||
|
||||
type T2 = [1, 2, 3][0:0];
|
||||
>T2 : []
|
||||
}
|
||||
{
|
||||
// - If the *object type* is an *Array Type*, then
|
||||
{
|
||||
// - If the signs of both the *start type* and *end type* agree, then return
|
||||
// a fixed-length *Tuple Type* with a minimum length of `0` for the difference between the *end type* and
|
||||
// the *start type* whose elements are the element type of the *object type*: `T[][0:1] -> [T?]`.
|
||||
type T1 = number[][0:2];
|
||||
>T1 : [number?, number?]
|
||||
|
||||
type T2 = number[][^2:^0];
|
||||
>T2 : [number?, number?]
|
||||
}
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type* and the *end type* is not, we can create a tuple of
|
||||
// `min(abs(start), end)` optional elements.
|
||||
type T1 = number[][^2:4];
|
||||
>T1 : [number?, number?]
|
||||
}
|
||||
{
|
||||
// - Otherwise, we cannot derive a fixed length: return the *object type* of the *Range Type*.
|
||||
type T1 = number[][0:^2];
|
||||
>T1 : number[]
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise, the *object type* is a *Tuple Type*:
|
||||
{
|
||||
// - If the *object type* has a rest element, then
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type*, return an *Array Type* for the union of each
|
||||
// *optional type* and the *rest element type*, along with the set of `n` right-most required
|
||||
// elements of the *object type* where `n` is the absolute value of the *start type*.
|
||||
type T1 = [1, 2, 3, ...4[]][^1:];
|
||||
>T1 : 4[]
|
||||
|
||||
type T2 = [1, 2, 3, ...4[]][^2:];
|
||||
>T2 : (3 | 4)[]
|
||||
|
||||
type T3 = [1, 2, 3, ...4[]][^3:];
|
||||
>T3 : (3 | 2 | 4)[]
|
||||
|
||||
type T4 = [1, 2, 3?, ...4[]][^1:];
|
||||
>T4 : (3 | 4 | undefined)[]
|
||||
|
||||
type T5 = [1, 2, 3?, ...4[]][^2:];
|
||||
>T5 : (3 | 2 | 4 | undefined)[]
|
||||
|
||||
type T6 = [1, 2, 3?, ...4[]][^3:];
|
||||
>T6 : (3 | 2 | 4 | undefined)[]
|
||||
}
|
||||
{
|
||||
// - If the *end type* is an *Inverse Offset Type*, return a *Tuple Type* of the elements of the
|
||||
// *object type* starting from the index at *start type*, but whose minimum length is reduced by the
|
||||
// absolute value of the *end type*.
|
||||
type T1 = [1, 2, 3, ...4[]][1:^1];
|
||||
>T1 : [2, 3?, ...4[]]
|
||||
|
||||
type T2 = [1, 2, 3, ...4[]][1:^2];
|
||||
>T2 : [2?, 3?, ...4[]]
|
||||
|
||||
type T3 = [1, 2, 3, ...4[]][1:^3];
|
||||
>T3 : [2?, 3?, ...4[]]
|
||||
|
||||
type T4 = [1, 2, 3?, ...4[]][1:^1];
|
||||
>T4 : [2?, (3 | undefined)?, ...4[]]
|
||||
|
||||
type T5 = [1, 2, 3?, ...4[]][1:^2];
|
||||
>T5 : [2?, (3 | undefined)?, ...4[]]
|
||||
|
||||
type T6 = [1, 2, 3?, ...4[]][1:^3];
|
||||
>T6 : [2?, (3 | undefined)?, ...4[]]
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise,
|
||||
{
|
||||
// - Clamp the *start type* and *end type* to values between `0` and the length of *object type*.
|
||||
type T1 = [1, 2, 3][^5:10];
|
||||
>T1 : [1, 2, 3]
|
||||
}
|
||||
{
|
||||
// - Return a *Tuple Type* for the elements of *object type* starting at *start type* and ending at
|
||||
// *end type*.
|
||||
type T1 = [1, 2, 3][1: 2];
|
||||
>T1 : [2]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/types/range/assignability.ts ===
|
||||
// - `S` is assignable to `T[X:Y]` if `S` is assignable to `C`, where `C` is the base constraint of `T[X:Y]` for writing.
|
||||
function f1<T extends [1, 2, 3, 4], U extends 1 | 2>(t: T[U:^1], s: [3] & [2, 3]) {
|
||||
>f1 : <T extends [1, 2, 3, 4], U extends 1 | 2>(t: T[U:^1], s: [3] & [2, 3]) => void
|
||||
>t : T[U:^1]
|
||||
>s : [3] & [2, 3]
|
||||
|
||||
t = s;
|
||||
>t = s : [3] & [2, 3]
|
||||
>t : T[U:^1]
|
||||
>s : [3] & [2, 3]
|
||||
}
|
||||
|
||||
// - `S[X:Y]` is assignable to `T[]` if `S[number]` is assignable to `T`.
|
||||
function f2<T, S extends [T, T, T]>(t: T[], s: S[0:2]) {
|
||||
>f2 : <T, S extends [T, T, T]>(t: T[], s: S[0:2]) => void
|
||||
>t : T[]
|
||||
>s : S[0:2]
|
||||
|
||||
t = s;
|
||||
>t = s : S[0:2]
|
||||
>t : T[]
|
||||
>s : S[0:2]
|
||||
}
|
||||
|
||||
// - `S[XS:YS]` is assignable to `T[XT:YT]` if `S` is assignable to `T`, `XS` is assignable to `XT`, and `YS` is assignable to `YT`.
|
||||
function f3<T extends [1 | 9, 2 | 8, 3 | 7, 4 | 6], S extends T>(t: T[1 | 2:3], s: S[1:3]) {
|
||||
>f3 : <T extends [1 | 9, 2 | 8, 3 | 7, 4 | 6], S extends T>(t: T[1 | 2:3], s: S[1:3]) => void
|
||||
>t : T[1 | 2:3]
|
||||
>s : S[1:3]
|
||||
|
||||
t = s;
|
||||
>t = s : S[1:3]
|
||||
>t : T[1 | 2:3]
|
||||
>s : S[1:3]
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
// @strict: true
|
||||
// Rules (from https://gist.github.com/rbuckton/53e335ce3d63686e229bc4ae25017756)
|
||||
|
||||
// @filename: parsing.ts
|
||||
// Parsing Rules
|
||||
type A = 0;
|
||||
type B = 1;
|
||||
type T0 = ^A;
|
||||
type T1 = ^A | B;
|
||||
type T2 = ^(A | B);
|
||||
type T3 = keyof ^A;
|
||||
type T4 = ^A[]; // error
|
||||
type T5 = (^A)[];
|
||||
|
||||
// @filename: semantics.ts
|
||||
// Semantic Rules
|
||||
// - An *Inverse Offset Type*'s *index type* is constrained to `string | number`.
|
||||
type T6 = ^0;
|
||||
type T7 = ^"0";
|
||||
type T8 = ^true; // error
|
||||
|
||||
// - The *Inverse Offset Type* of an *Inverse Offset Type* `I` is the *index type* of `I`: `^^I -> I`
|
||||
type T9 = ^^0; // inverse rule (double negation = non-negated)
|
||||
|
||||
// - An *Inverse Offset Type* for negative *index type* is instead a *Literal Type* for the absolute value of the *index type*: `^-1 -> 1`
|
||||
type T10 = ^-1; // inverse rule (double negation = non-negated)
|
||||
|
||||
// - If the *index type* of an *Inverse Offset Type* is a union, the *Inverse Offset Type* is distributed over the union: `^(A | B) -> ^A | ^B`
|
||||
type T11 = ^(0 | 1); // distributes
|
||||
|
||||
// - An *Inverse Offset Type* is deferred until applied as the *index type* of an *Indexed Access Type* or *Range Type*.
|
||||
type T12 = ^0;
|
||||
|
||||
// - An *Inverse Offset Type* is deferred if its *index type* is generic.
|
||||
type T13<A extends string | number> = ^A;
|
||||
type T14 = T13<1>;
|
||||
|
||||
type AR = [1, 2, 3];
|
||||
type X = AR[^1]; // 3
|
||||
|
||||
// @filename: assignability.ts
|
||||
// Assignability Rules
|
||||
// - `^S` is assignable to `string | number`.
|
||||
type Constrained0<T extends string | number> = never;
|
||||
type Constrained1<T extends boolean | bigint | symbol | undefined | null | object> = never;
|
||||
type T15 = Constrained0<^0>;
|
||||
type T16 = Constrained1<^0>; // error
|
||||
|
||||
// - `^S` is assignable to `^T` if `S` is assignable to `T`.
|
||||
function f<T extends string | number, S extends T>(s: ^S, t: ^T) {
|
||||
t = s;
|
||||
}
|
||||
189
tests/cases/conformance/types/range/ranges.ts
Normal file
189
tests/cases/conformance/types/range/ranges.ts
Normal file
@ -0,0 +1,189 @@
|
||||
// @strict: true
|
||||
// Rules (from https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c)
|
||||
|
||||
// @filename: semantics.ts
|
||||
// Semantic Rules
|
||||
{
|
||||
// - The result of a *Range Type* has the same mutability as its *object type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = (readonly [1, 2, 3])[0:2];
|
||||
}
|
||||
|
||||
{
|
||||
// - The *object type* of a *Range Type* is constrained to be an *Array Type* or a *Tuple Type*.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = number[][0:2];
|
||||
type T3 = {}[0:2]; // error
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* and *end type* of a *Range Type* are constrained to `string | number`.
|
||||
type T1 = [1, 2, 3][0:2];
|
||||
type T2 = [1, 2, 3]["0":"2"];
|
||||
type T3 = [1, 2, 3][true:false]; // error
|
||||
}
|
||||
|
||||
{
|
||||
// - The *start type* of a *Range Type* is optional. If not present, the *lower bound type* (`0`) is used.
|
||||
type T2 = [1, 2, 3][:1];
|
||||
|
||||
// - The *end type* of a *Range Type* is optional. If not present, the *upper bound type* (`^0`) is used.
|
||||
type T3 = [1, 2, 3][1:];
|
||||
type T1 = [1, 2, 3][:];
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *start type* or *end type* of a *Range Type* are negative-valued *Numeric Literal Types* (or numeric index-valued *String Literal Types*), they are treated
|
||||
// as an *Inverse Offset Type* for the absolute value of the numeric index value of the respective type.
|
||||
// - NOTE: This does not work for `-0` as JavaScript generally treats `-0` and `0` as the same value except for a few small corner cases and we must
|
||||
// align with this behavior.
|
||||
type T1 = [1, 2, 3, 4][1:-1];
|
||||
type T2 = [1, 2, 3, 4][1:-0];
|
||||
}
|
||||
|
||||
{
|
||||
// - If the *object type* is a "generic object type", or if either of the *start type* or *end type* are
|
||||
// "generic index types", then the operation is deferred until it they can be instantiated.
|
||||
type T1<A extends any[]> = A[0:2];
|
||||
type T2 = T1<[1, 2, 3]>;
|
||||
type T3 = T1<number[]>;
|
||||
|
||||
type T4<X extends number> = [1, 2, 3][X:];
|
||||
type T5 = T4<1>;
|
||||
|
||||
type T6<Y extends number> = [1, 2, 3][:Y];
|
||||
type T7 = T6<^1>;
|
||||
}
|
||||
|
||||
{
|
||||
// - If either the *object type*, *start type*, or *end type* are *Union Types*, the result is distributed over
|
||||
// each constituent in the following manner:
|
||||
// - The *object type* is distributed over any *Inverse Offset Type* constituent of *start type* or *end type*.
|
||||
// This is necessary as an *Inverse Offset Type* can have a different outcome depending on the *object type*
|
||||
// it is resolved against.
|
||||
// - The *start type* and *end type* are distributed over each constituent of the *object type*.
|
||||
// - The *object type* is distributed over each constituent of the *start type* and *end type*.
|
||||
// - The results of the distribution are either an *Intersection Type* (if the *Range Type* was a "write"
|
||||
// location), or a *Union Type* (if the *Range Type* was a "read" location).
|
||||
type T1 = ([1, 2, 3] | [2, 3, 4])[0:2];
|
||||
type T2 = [1, 2, 3][0|1:2];
|
||||
type T3 = [1, 2, 3][0:1|2];
|
||||
type T4 = ([1, 2, 3] | [2, 3, 4])[0|1:2|3];
|
||||
type T5 = ([1, 2, 3] | [9, 8])[0:^1];
|
||||
}
|
||||
|
||||
{
|
||||
// - Otherwise (or for each constituent of the distribution),
|
||||
{
|
||||
// - If neither the *start type* nor the *end type* are *String Literal Types* or a *Numeric Literal Types*, then
|
||||
// - Return an *Array Type* for the union of each element of the *object type*: `T[any:any] -> T[number][]`.
|
||||
type T1 = [1, 2, 3][number:number];
|
||||
type T2 = [1, 2, 3][string:string];
|
||||
type T3 = [1, 2, 3][any:any];
|
||||
type T4 = [1, 2, 3][never:never];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *end type*, but with a *start type* of `0`: `T[any:Y] -> T[:Y][number][]`.
|
||||
type T1 = [1, 2, 3][number:2];
|
||||
type T2 = [1, 2, 3][string:2];
|
||||
type T3 = [1, 2, 3][any:2];
|
||||
type T4 = [1, 2, 3][never:2];
|
||||
}
|
||||
{
|
||||
// - If the *end type* is neither a *String Literal Type* nor a *Numeric Literal Type*, then
|
||||
// - Return an *Array Type* for the union of each element of the *Range Type* for the same *object type* and
|
||||
// *start type*, but with an *end type* of `^0`: `T[X:any] -> T[X:][number][]`.
|
||||
type T1 = [1, 2, 3][1:number];
|
||||
type T2 = [1, 2, 3][1:string];
|
||||
type T3 = [1, 2, 3][1:any];
|
||||
type T4 = [1, 2, 3][1:never];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *lower bound type* (`0`) and the *end type* is the *upper bound type* (`^0`),
|
||||
// then we are including all elements: return the *object type* of the *Range Type*.
|
||||
type T1 = [1, 2, 3][0:^0];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is the *upper bound type* (`^0`) or the *end type* is the *lower bound type* (`0`),
|
||||
// then we are including no elements: return the empty *Tuple Type* (`[]`).
|
||||
type T1 = [1, 2, 3][^0:^0];
|
||||
type T2 = [1, 2, 3][0:0];
|
||||
}
|
||||
{
|
||||
// - If the *object type* is an *Array Type*, then
|
||||
{
|
||||
// - If the signs of both the *start type* and *end type* agree, then return
|
||||
// a fixed-length *Tuple Type* with a minimum length of `0` for the difference between the *end type* and
|
||||
// the *start type* whose elements are the element type of the *object type*: `T[][0:1] -> [T?]`.
|
||||
type T1 = number[][0:2];
|
||||
type T2 = number[][^2:^0];
|
||||
}
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type* and the *end type* is not, we can create a tuple of
|
||||
// `min(abs(start), end)` optional elements.
|
||||
type T1 = number[][^2:4];
|
||||
}
|
||||
{
|
||||
// - Otherwise, we cannot derive a fixed length: return the *object type* of the *Range Type*.
|
||||
type T1 = number[][0:^2];
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise, the *object type* is a *Tuple Type*:
|
||||
{
|
||||
// - If the *object type* has a rest element, then
|
||||
{
|
||||
// - If the *start type* is an *Inverse Offset Type*, return an *Array Type* for the union of each
|
||||
// *optional type* and the *rest element type*, along with the set of `n` right-most required
|
||||
// elements of the *object type* where `n` is the absolute value of the *start type*.
|
||||
type T1 = [1, 2, 3, ...4[]][^1:];
|
||||
type T2 = [1, 2, 3, ...4[]][^2:];
|
||||
type T3 = [1, 2, 3, ...4[]][^3:];
|
||||
type T4 = [1, 2, 3?, ...4[]][^1:];
|
||||
type T5 = [1, 2, 3?, ...4[]][^2:];
|
||||
type T6 = [1, 2, 3?, ...4[]][^3:];
|
||||
}
|
||||
{
|
||||
// - If the *end type* is an *Inverse Offset Type*, return a *Tuple Type* of the elements of the
|
||||
// *object type* starting from the index at *start type*, but whose minimum length is reduced by the
|
||||
// absolute value of the *end type*.
|
||||
type T1 = [1, 2, 3, ...4[]][1:^1];
|
||||
type T2 = [1, 2, 3, ...4[]][1:^2];
|
||||
type T3 = [1, 2, 3, ...4[]][1:^3];
|
||||
type T4 = [1, 2, 3?, ...4[]][1:^1];
|
||||
type T5 = [1, 2, 3?, ...4[]][1:^2];
|
||||
type T6 = [1, 2, 3?, ...4[]][1:^3];
|
||||
}
|
||||
}
|
||||
{
|
||||
// - Otherwise,
|
||||
{
|
||||
// - Clamp the *start type* and *end type* to values between `0` and the length of *object type*.
|
||||
type T1 = [1, 2, 3][^5:10];
|
||||
}
|
||||
{
|
||||
// - Return a *Tuple Type* for the elements of *object type* starting at *start type* and ending at
|
||||
// *end type*.
|
||||
type T1 = [1, 2, 3][1: 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: assignability.ts
|
||||
// - `S` is assignable to `T[X:Y]` if `S` is assignable to `C`, where `C` is the base constraint of `T[X:Y]` for writing.
|
||||
function f1<T extends [1, 2, 3, 4], U extends 1 | 2>(t: T[U:^1], s: [3] & [2, 3]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
// - `S[X:Y]` is assignable to `T[]` if `S[number]` is assignable to `T`.
|
||||
function f2<T, S extends [T, T, T]>(t: T[], s: S[0:2]) {
|
||||
t = s;
|
||||
}
|
||||
|
||||
// - `S[XS:YS]` is assignable to `T[XT:YT]` if `S` is assignable to `T`, `XS` is assignable to `XT`, and `YS` is assignable to `YT`.
|
||||
function f3<T extends [1 | 9, 2 | 8, 3 | 7, 4 | 6], S extends T>(t: T[1 | 2:3], s: S[1:3]) {
|
||||
t = s;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user