Merge pull request #17870 from Microsoft/fix-getConstraintOfIndexedAccess

Fix getConstraintOfIndexedAccess
This commit is contained in:
Arthur Ozga 2017-08-18 11:32:48 -07:00 committed by GitHub
commit 439cdca52f
9 changed files with 334 additions and 8 deletions

View file

@ -5911,7 +5911,13 @@ namespace ts {
return transformed;
}
const baseObjectType = getBaseConstraintOfType(type.objectType);
const baseIndexType = getBaseConstraintOfType(type.indexType);
const keepTypeParameterForMappedType = baseObjectType && getObjectFlags(baseObjectType) & ObjectFlags.Mapped && type.indexType.flags & TypeFlags.TypeParameter;
const baseIndexType = !keepTypeParameterForMappedType && getBaseConstraintOfType(type.indexType);
if (baseObjectType && baseIndexType === stringType && !getIndexInfoOfType(baseObjectType, IndexKind.String)) {
// getIndexedAccessType returns `any` for X[string] where X doesn't have an index signature.
// instead, return undefined so that the indexed access check fails
return undefined;
}
return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined;
}
@ -5961,8 +5967,9 @@ namespace ts {
function computeBaseConstraint(t: Type): Type {
if (t.flags & TypeFlags.TypeParameter) {
const constraint = getConstraintFromTypeParameter(<TypeParameter>t);
return (<TypeParameter>t).isThisType ? constraint :
constraint ? getBaseConstraint(constraint) : undefined;
return (t as TypeParameter).isThisType || !constraint ?
constraint :
getBaseConstraint(constraint);
}
if (t.flags & TypeFlags.UnionOrIntersection) {
const types = (<UnionOrIntersectionType>t).types;
@ -5990,9 +5997,6 @@ namespace ts {
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
}
if (isGenericMappedType(t)) {
return emptyObjectType;
}
return t;
}
}
@ -9289,7 +9293,7 @@ namespace ts {
}
else if (target.flags & TypeFlags.IndexedAccess) {
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
// A is the apparent type of S.
// A is the apparent type of T.
const constraint = getConstraintOfType(<IndexedAccessType>target);
if (constraint) {
if (result = isRelatedTo(source, constraint, reportErrors)) {

View file

@ -718,7 +718,7 @@ namespace ts {
export function sum<T extends Record<K, number>, K extends string>(array: T[], prop: K): number {
let result = 0;
for (const v of array) {
// Note: we need the following type assertion because of GH #17069
// TODO: Remove the following type assertion once the fix for #17069 is merged
result += v[prop] as number;
}
return result;

View file

@ -0,0 +1,27 @@
//// [additionOperatorWithConstrainedTypeParameter.ts]
// test for #17069
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
n = n + v[k];
n += v[k]; // += should work the same way
}
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
for (const v of vs) {
n = n + v[k];
n += v[k];
}
}
//// [additionOperatorWithConstrainedTypeParameter.js]
// test for #17069
function sum(n, v, k) {
n = n + v[k];
n += v[k]; // += should work the same way
}
function realSum(n, vs, k) {
for (var _i = 0, vs_1 = vs; _i < vs_1.length; _i++) {
var v = vs_1[_i];
n = n + v[k];
n += v[k];
}
}

View file

@ -0,0 +1,54 @@
=== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithConstrainedTypeParameter.ts ===
// test for #17069
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
>sum : Symbol(sum, Decl(additionOperatorWithConstrainedTypeParameter.ts, 0, 0))
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 13))
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 13))
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 41))
n = n + v[k];
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
n += v[k]; // += should work the same way
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 60))
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 70))
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 1, 76))
}
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
>realSum : Symbol(realSum, Decl(additionOperatorWithConstrainedTypeParameter.ts, 4, 1))
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 17))
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
>vs : Symbol(vs, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 74))
>T : Symbol(T, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 17))
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
>K : Symbol(K, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 45))
for (const v of vs) {
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
>vs : Symbol(vs, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 74))
n = n + v[k];
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
n += v[k];
>n : Symbol(n, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 64))
>v : Symbol(v, Decl(additionOperatorWithConstrainedTypeParameter.ts, 6, 14))
>k : Symbol(k, Decl(additionOperatorWithConstrainedTypeParameter.ts, 5, 83))
}
}

View file

@ -0,0 +1,64 @@
=== tests/cases/conformance/expressions/binaryOperators/additionOperator/additionOperatorWithConstrainedTypeParameter.ts ===
// test for #17069
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
>sum : <T extends Record<K, number>, K extends string>(n: number, v: T, k: K) => void
>T : T
>Record : Record<K, T>
>K : K
>K : K
>n : number
>v : T
>T : T
>k : K
>K : K
n = n + v[k];
>n = n + v[k] : number
>n : number
>n + v[k] : number
>n : number
>v[k] : T[K]
>v : T
>k : K
n += v[k]; // += should work the same way
>n += v[k] : number
>n : number
>v[k] : T[K]
>v : T
>k : K
}
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
>realSum : <T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) => void
>T : T
>Record : Record<K, T>
>K : K
>K : K
>n : number
>vs : T[]
>T : T
>k : K
>K : K
for (const v of vs) {
>v : T
>vs : T[]
n = n + v[k];
>n = n + v[k] : number
>n : number
>n + v[k] : number
>n : number
>v[k] : T[K]
>v : T
>k : K
n += v[k];
>n += v[k] : number
>n : number
>v[k] : T[K]
>v : T
>k : K
}
}

View file

@ -0,0 +1,67 @@
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(3,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(6,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(9,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(12,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(15,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(18,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(21,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(24,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(27,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts(30,5): error TS2322: Type 'string' is not assignable to type 'T[P]'.
Type 'string' is not assignable to type 'number'.
==== tests/cases/conformance/types/nonPrimitive/nonPrimitiveConstraintOfIndexAccessType.ts (10 errors) ====
// test for #15371
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
}
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
~~
!!! error TS2322: Type 'string' is not assignable to type 'T[P]'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
}

View file

@ -0,0 +1,67 @@
//// [nonPrimitiveConstraintOfIndexAccessType.ts]
// test for #15371
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
//// [nonPrimitiveConstraintOfIndexAccessType.js]
"use strict";
// test for #15371
function f(s, tp) {
tp = s;
}
function g(s, tp) {
tp = s;
}
function h(s, tp) {
tp = s;
}
function i(s, tp) {
tp = s;
}
function j(s, tp) {
tp = s;
}
function k(s, tp) {
tp = s;
}
function o(s, tp) {
tp = s;
}
function l(s, tp) {
tp = s;
}
function m(s, tp) {
tp = s;
}
function n(s, tp) {
tp = s;
}

View file

@ -0,0 +1,11 @@
// test for #17069
function sum<T extends Record<K, number>, K extends string>(n: number, v: T, k: K) {
n = n + v[k];
n += v[k]; // += should work the same way
}
function realSum<T extends Record<K, number>, K extends string>(n: number, vs: T[], k: K) {
for (const v of vs) {
n = n + v[k];
n += v[k];
}
}

View file

@ -0,0 +1,32 @@
// @strict: true
// test for #15371
function f<T extends object, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function g<T extends null, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function h<T extends undefined, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function i<T extends void, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function j<T extends never, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function k<T extends number, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function o<T extends string, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function l<T extends {}, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function m<T extends { a: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}
function n<T extends { [s: string]: number }, P extends keyof T>(s: string, tp: T[P]): void {
tp = s;
}