Skip to content

Commit

Permalink
Retain errorType when it is used as a marker for the lack of a constr…
Browse files Browse the repository at this point in the history
…aint
  • Loading branch information
weswigham committed Aug 20, 2018
1 parent 02bd1a1 commit ef9f6ee
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 710 deletions.
34 changes: 18 additions & 16 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6908,7 +6908,7 @@ namespace ts {
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
const objectType = getBaseConstraintOfType(type.objectType) || type.objectType;
const indexType = getBaseConstraintOfType(type.indexType) || type.indexType;
const constraint = !isGenericObjectType(objectType) && !isGenericIndexType(indexType) ? getIndexedAccessType(objectType, indexType) : undefined;
const constraint = !isGenericObjectType(objectType) && !isGenericIndexType(indexType) ? getIndexedAccessType(objectType, indexType, /*accessNode*/ undefined, errorType) : undefined;
return constraint && constraint !== errorType ? constraint : undefined;
}

Expand Down Expand Up @@ -7059,7 +7059,7 @@ 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) : undefined;
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType, /*accessNode*/ undefined, errorType) : undefined;
return baseIndexedAccess && baseIndexedAccess !== errorType ? getBaseConstraint(baseIndexedAccess) : undefined;
}
if (t.flags & TypeFlags.Conditional) {
Expand Down Expand Up @@ -9144,7 +9144,7 @@ namespace ts {
return false;
}

function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | undefined, cacheSymbol: boolean) {
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | undefined, cacheSymbol: boolean, missingType: Type) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
const propName = isTypeUsableAsLateBoundName(indexType) ? getLateBoundNameFromType(indexType) :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
Expand Down Expand Up @@ -9216,7 +9216,7 @@ namespace ts {
}
}
}
return accessNode ? errorType : unknownType;
return missingType;
}
}
if (isJSLiteralType(objectType)) {
Expand All @@ -9237,7 +9237,7 @@ namespace ts {
if (isTypeAny(indexType)) {
return indexType;
}
return accessNode ? errorType : unknownType;
return missingType;
}

function isGenericObjectType(type: Type): boolean {
Expand Down Expand Up @@ -9290,7 +9290,7 @@ namespace ts {
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
}

function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode, missingType = accessNode ? errorType : unknownType): Type {
if (objectType === wildcardType || indexType === wildcardType) {
return wildcardType;
}
Expand Down Expand Up @@ -9318,18 +9318,15 @@ namespace ts {
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Boolean)) {
const propTypes: Type[] = [];
for (const t of (<UnionType>indexType).types) {
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
if (propType === unknownType) {
return unknownType;
}
if (propType === errorType) {
return errorType;
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false, missingType);
if (propType === missingType) {
return missingType;
}
propTypes.push(propType);
}
return getUnionType(propTypes);
}
return getPropertyTypeForIndexType(apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true);
return getPropertyTypeForIndexType(apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true, missingType);
}

function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
Expand Down Expand Up @@ -10476,8 +10473,13 @@ namespace ts {
return false;
}

function isOrHasGenericConditional(type: Type): boolean {
return !!(type.flags & TypeFlags.Conditional || (type.flags & TypeFlags.Intersection && some((type as IntersectionType).types, isOrHasGenericConditional)));
}

function elaborateError(node: Expression | undefined, source: Type, target: Type): boolean {
if (!node) return false;
if (isOrHasGenericConditional(target)) return false;
switch (node.kind) {
case SyntaxKind.JsxExpression:
case SyntaxKind.ParenthesizedExpression:
Expand Down Expand Up @@ -10510,9 +10512,9 @@ 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 sourcePropType = getIndexedAccessType(source, nameType);
const targetPropType = getIndexedAccessType(target, nameType);
if (!isTypeAssignableTo(sourcePropType, targetPropType)) {
const sourcePropType = getIndexedAccessType(source, nameType, /*accessNode*/ undefined, errorType);
const targetPropType = getIndexedAccessType(target, nameType, /*accessNode*/ undefined, errorType);
if (sourcePropType !== errorType && targetPropType !== errorType && !isTypeAssignableTo(sourcePropType, targetPropType)) {
const elaborated = next && elaborateError(next, sourcePropType, targetPropType);
if (elaborated) {
reportedError = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts(7,5): error TS2418: Type of computed property's value is 'string', which is not assignable to type 'boolean'.
tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts(8,5): error TS2418: Type of computed property's value is 'number', which is not assignable to type 'boolean'.
tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts(6,5): error TS2322: Type '{ [x: number]: string | number; }' is not assignable to type 'I'.
Index signatures are incompatible.
Type 'string | number' is not assignable to type 'boolean'.
Type 'string' is not assignable to type 'boolean'.


==== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts (2 errors) ====
==== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts (1 errors) ====
interface I {
[s: string]: boolean;
[s: number]: boolean;
}

var o: I = {
~
!!! error TS2322: Type '{ [x: number]: string | number; }' is not assignable to type 'I'.
!!! error TS2322: Index signatures are incompatible.
!!! error TS2322: Type 'string | number' is not assignable to type 'boolean'.
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
[+"foo"]: "",
~~~~~~~~
!!! error TS2418: Type of computed property's value is 'string', which is not assignable to type 'boolean'.
!!! related TS6501 tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts:2:5: The expected type comes from this index signature.
[+"bar"]: 0
~~~~~~~~
!!! error TS2418: Type of computed property's value is 'number', which is not assignable to type 'boolean'.
!!! related TS6501 tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES5.ts:2:5: The expected type comes from this index signature.
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts(7,5): error TS2418: Type of computed property's value is 'string', which is not assignable to type 'boolean'.
tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts(8,5): error TS2418: Type of computed property's value is 'number', which is not assignable to type 'boolean'.
tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts(6,5): error TS2322: Type '{ [x: number]: string | number; }' is not assignable to type 'I'.
Index signatures are incompatible.
Type 'string | number' is not assignable to type 'boolean'.
Type 'string' is not assignable to type 'boolean'.


==== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts (2 errors) ====
==== tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts (1 errors) ====
interface I {
[s: string]: boolean;
[s: number]: boolean;
}

var o: I = {
~
!!! error TS2322: Type '{ [x: number]: string | number; }' is not assignable to type 'I'.
!!! error TS2322: Index signatures are incompatible.
!!! error TS2322: Type 'string | number' is not assignable to type 'boolean'.
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
[+"foo"]: "",
~~~~~~~~
!!! error TS2418: Type of computed property's value is 'string', which is not assignable to type 'boolean'.
!!! related TS6501 tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts:2:5: The expected type comes from this index signature.
[+"bar"]: 0
~~~~~~~~
!!! error TS2418: Type of computed property's value is 'number', which is not assignable to type 'boolean'.
!!! related TS6501 tests/cases/conformance/es6/computedProperties/computedPropertyNamesContextualType9_ES6.ts:2:5: The expected type comes from this index signature.
}
Loading

0 comments on commit ef9f6ee

Please sign in to comment.