Add getIndexedAccessOrUndefined function

This commit is contained in:
Anders Hejlsberg
2019-03-31 16:52:50 -07:00
parent 5de3ee8ebc
commit 95c8a92b88
2 changed files with 51 additions and 40 deletions

View File

@@ -7195,8 +7195,7 @@ namespace ts {
return type;
}
if (type.flags & TypeFlags.Index) {
const keys = getIndexType(getApparentType((<IndexType>type).type));
return isIndexType ? filterType(keys, t => !!(t.flags & TypeFlags.Literal)) : keys;
return getIndexType(getApparentType((<IndexType>type).type));
}
if (type.flags & TypeFlags.Conditional) {
if ((<ConditionalType>type).root.isDistributive) {
@@ -7491,8 +7490,8 @@ namespace ts {
function getConstraintFromIndexedAccess(type: IndexedAccessType) {
const objectType = getConstraintOfType(type.objectType) || type.objectType;
if (objectType !== type.objectType) {
const constraint = getIndexedAccessType(objectType, type.indexType, /*accessNode*/ undefined, errorType);
if (constraint && constraint !== errorType) {
const constraint = getIndexedAccessTypeOrUndefined(objectType, type.indexType);
if (constraint) {
return constraint;
}
}
@@ -7686,8 +7685,8 @@ namespace ts {
if (t.flags & TypeFlags.IndexedAccess) {
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType, /*accessNode*/ undefined, errorType) : undefined;
return baseIndexedAccess && baseIndexedAccess !== errorType ? getBaseConstraint(baseIndexedAccess) : undefined;
const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType);
return baseIndexedAccess && getBaseConstraint(baseIndexedAccess);
}
if (t.flags & TypeFlags.Conditional) {
const constraint = getConstraintFromConditionalType(<ConditionalType>t);
@@ -9875,7 +9874,7 @@ namespace ts {
return false;
}
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean, missingType: Type) {
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, cacheSymbol: boolean) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
const propName = isTypeUsableAsPropertyName(indexType) ?
getPropertyNameFromType(indexType) :
@@ -9892,7 +9891,7 @@ namespace ts {
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
if (isAssignmentTarget(accessExpression) && (isReferenceToReadonlyEntity(accessExpression, prop) || isReferenceThroughNamespaceImport(accessExpression))) {
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, symbolToString(prop));
return missingType;
return undefined;
}
if (cacheSymbol) {
getNodeLinks(accessNode!).resolvedSymbol = prop;
@@ -9921,16 +9920,19 @@ namespace ts {
if (objectType.flags & (TypeFlags.Any | TypeFlags.Never)) {
return objectType;
}
const indexInfo = accessExpression && isAssignmentTarget(accessExpression) && isGenericObjectType(originalObjectType) ? undefined :
isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
getIndexInfoOfType(objectType, IndexKind.String) ||
undefined;
const isAssignment = accessExpression && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression));
if (isAssignment && maybeTypeOfKind(originalObjectType, TypeFlags.Instantiable)) {
error(accessExpression, Diagnostics.Type_0_cannot_be_indexed_by_type_1, typeToString(originalObjectType), typeToString(indexType));
return undefined;
}
const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
getIndexInfoOfType(objectType, IndexKind.String);
if (indexInfo) {
if (accessNode && !isTypeAssignableToKind(indexType, TypeFlags.String | TypeFlags.Number)) {
const indexNode = getIndexNodeForAccessExpression(accessNode);
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
}
else if (accessExpression && indexInfo.isReadonly && (isAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression))) {
else if (isAssignment && indexInfo.isReadonly) {
error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
}
return indexInfo.type;
@@ -9964,7 +9966,7 @@ namespace ts {
}
}
}
return missingType;
return undefined;
}
}
if (isJSLiteralType(objectType)) {
@@ -9985,7 +9987,7 @@ namespace ts {
if (isTypeAny(indexType)) {
return indexType;
}
return missingType;
return undefined;
}
function getIndexNodeForAccessExpression(accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression) {
@@ -10076,7 +10078,11 @@ namespace ts {
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
}
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, missingType = accessNode ? errorType : unknownType): Type {
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, writing?: boolean): Type {
return getIndexedAccessTypeOrUndefined(objectType, indexType, accessNode, writing) || (accessNode ? errorType : unknownType);
}
function getIndexedAccessTypeOrUndefined(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, writing?: boolean): Type | undefined {
if (objectType === wildcardType || indexType === wildcardType) {
return wildcardType;
}
@@ -10105,25 +10111,25 @@ namespace ts {
const propTypes: Type[] = [];
let wasMissingProp = false;
for (const t of (<UnionType>indexType).types) {
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, accessNode, /*cacheSymbol*/ false, missingType);
if (propType === missingType) {
if (!accessNode) {
// If there's no error node, we can immeditely stop, since error reporting is off
return missingType;
}
else {
// Otherwise we set a flag and return at the end of the loop so we still mark all errors
wasMissingProp = true;
}
const propType = getPropertyTypeForIndexType(objectType, apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
if (propType) {
propTypes.push(propType);
}
else if (!accessNode) {
// If there's no error node, we can immeditely 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
wasMissingProp = true;
}
propTypes.push(propType);
}
if (wasMissingProp) {
return missingType;
return undefined;
}
return !accessNode || getAssignmentTargetKind(accessNode) === AssignmentKind.None ? getUnionType(propTypes) : getIntersectionType(propTypes);
return writing ? getIntersectionType(propTypes) : getUnionType(propTypes);
}
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true, missingType);
return getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true);
}
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -11512,10 +11518,10 @@ namespace ts {
let reportedError = false;
for (let status = iterator.next(); !status.done; status = iterator.next()) {
const { errorNode: prop, innerExpression: next, nameType, errorMessage } = status.value;
const targetPropType = getIndexedAccessType(target, nameType, /*accessNode*/ undefined, errorType);
if (targetPropType === errorType || targetPropType.flags & TypeFlags.IndexedAccess) continue; // Don't elaborate on indexes on generic variables
const sourcePropType = getIndexedAccessType(source, nameType, /*accessNode*/ undefined, errorType);
if (sourcePropType !== errorType && targetPropType !== errorType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
const targetPropType = getIndexedAccessTypeOrUndefined(target, nameType);
if (!targetPropType || targetPropType.flags & TypeFlags.IndexedAccess) continue; // Don't elaborate on indexes on generic variables
const sourcePropType = getIndexedAccessTypeOrUndefined(source, nameType);
if (sourcePropType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined);
if (elaborated) {
reportedError = true;
@@ -12819,9 +12825,7 @@ namespace ts {
if (indexType.flags & TypeFlags.StructuredOrInstantiable) {
const keyType = getLowerBoundOfKeyType(indexType, /*isIndexType*/ true);
if (keyType !== indexType && !(keyType.flags & TypeFlags.Never)) {
const targetType = keyType.flags & TypeFlags.Union ?
getIntersectionType(map((<UnionType>keyType).types, t => getIndexedAccessType(objectType, t))) :
getIndexedAccessType(objectType, keyType);
const targetType = getIndexedAccessType(objectType, keyType, /*accessNode*/ undefined, /*writing*/ true);
if (result = isRelatedTo(source, targetType, reportErrors)) {
return result;
}
@@ -12830,7 +12834,8 @@ namespace ts {
else {
const constraint = getConstraintOfType(objectType);
if (constraint) {
if (result = isRelatedTo(source, getIndexedAccessType(constraint, indexType), reportErrors)) {
const targetType = getIndexedAccessType(constraint, indexType, /*accessNode*/ undefined, /*writing*/ true);
if (result = isRelatedTo(source, targetType, reportErrors)) {
return result;
}
}
@@ -19975,7 +19980,9 @@ namespace ts {
return errorType;
}
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType, node), node);
const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType;
const indexedAccessType = getIndexedAccessType(objectType, effectiveIndexType, node, isAssignmentTarget(node));
return checkIndexedAccessIndexType(indexedAccessType, node);
}
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -24570,7 +24577,7 @@ namespace ts {
return type;
}
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
return type;
return errorType;
}
function checkIndexedAccessType(node: IndexedAccessTypeNode) {

View File

@@ -2152,6 +2152,10 @@
"category": "Error",
"code": 2593
},
"Type '{0}' cannot be indexed by type '{1}'.": {
"category": "Error",
"code": 2594
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600